@sap/cds-compiler 5.6.0 → 5.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/bin/cdsse.js +1 -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 +169 -144
  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 +2 -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 +2 -2
  25. package/lib/edm/edmUtils.js +2 -1
  26. package/lib/gen/BaseParser.js +32 -32
  27. package/lib/gen/CdlParser.js +1526 -1488
  28. package/lib/json/from-csn.js +2 -0
  29. package/lib/json/to-csn.js +13 -4
  30. package/lib/language/docCommentParser.js +11 -5
  31. package/lib/language/errorStrategy.js +3 -3
  32. package/lib/language/genericAntlrParser.js +2 -0
  33. package/lib/model/csnUtils.js +6 -1
  34. package/lib/optionProcessor.js +5 -1
  35. package/lib/parsers/AstBuildingParser.js +161 -73
  36. package/lib/parsers/CdlGrammar.g4 +129 -85
  37. package/lib/parsers/Lexer.js +5 -3
  38. package/lib/parsers/index.js +1 -1
  39. package/lib/render/toCdl.js +6 -5
  40. package/lib/render/toHdbcds.js +1 -1
  41. package/lib/render/toSql.js +5 -3
  42. package/lib/render/utils/common.js +19 -6
  43. package/lib/render/utils/delta.js +1 -3
  44. package/lib/render/utils/standardDatabaseFunctions.js +576 -0
  45. package/lib/transform/addTenantFields.js +2 -1
  46. package/lib/transform/db/flattening.js +18 -77
  47. package/lib/transform/db/groupByOrderBy.js +2 -2
  48. package/lib/transform/db/rewriteCalculatedElements.js +14 -19
  49. package/lib/transform/db/temporal.js +2 -1
  50. package/lib/transform/odata/adaptAnnotationRefs.js +79 -0
  51. package/lib/transform/odata/createForeignKeys.js +4 -71
  52. package/lib/transform/odata/flattening.js +11 -1
  53. package/lib/transform/transformUtils.js +20 -85
  54. package/package.json +2 -1
  55. 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
 
@@ -127,7 +128,7 @@ usingDeclaration[ source ] locals[ decl = { kind: 'using' } ] // TODO: XsnArtifa
127
128
  '{' { $decl.usings = this.createArray(); }
128
129
  ( usingProxy[ $decl, { kind: 'using' } ]
129
130
  ( ',' | <exitLoop> )
130
- )+
131
+ )*
131
132
  '}'<prepare=afterBrace>
132
133
  { this.finalizeDictOrArray( $decl.usings ); }
133
134
  ( FROM String
@@ -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,7 @@ mixinElementDef[ outer ] locals[ art = new XsnArtifact() ]
507
519
  ( assoc=ASSOCIATION cardinality[ $art ]? TO
508
520
  | assoc=COMPOSITION cardinality[ $art ]? OF
509
521
  )
510
- ( <cond=noRepeatedCardinality> card=ONE/MANY )?
522
+ ( <guard=noRepeatedCardinality> card=ONE/MANY )?
511
523
  target=simplePath
512
524
  { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
513
525
  ON expr=condition { $art.on = $expr; }
@@ -527,7 +539,7 @@ annotateArtifact[ art, outer ]
527
539
  annotateElementsBlock[ $art ]?
528
540
  | // definition annotation
529
541
  keyword=WITH?
530
- // <cond=noRuleExitAfterWith>), or as rule option,
542
+ // <guard=noRuleExitAfterWith>), or as rule option,
531
543
  // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
532
544
  { this.addExtension( $art, $outer, 'annotate', $name ); }
533
545
  { this.docComment( $art ); } annoAssignStd[ $art ]*
@@ -598,7 +610,7 @@ extendService[ art, outer ]
598
610
  name=namePath[ 'ExtService' ]
599
611
  { $art.name = $name; $outer.extensions.push( $art ); }
600
612
  keyword=WITH?
601
- // <cond=noRuleExitAfterWith>), or as rule option,
613
+ // <guard=noRuleExitAfterWith>), or as rule option,
602
614
  // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
603
615
  { this.docComment( $art ); } annoAssignStd[ $art ]*
604
616
  artifactsBlock[ $art ]?
@@ -611,7 +623,7 @@ extendContext[ art, outer ]
611
623
  name=namePath[ 'ExtContext' ]
612
624
  { $art.name = $name; $outer.extensions.push( $art ); }
613
625
  keyword=WITH?
614
- // <cond=noRuleExitAfterWith>), or as rule option,
626
+ // <guard=noRuleExitAfterWith>), or as rule option,
615
627
  // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
616
628
  { this.docComment( $art ); } annoAssignStd[ $art ]*
617
629
  artifactsBlock[ $art ]?
@@ -681,7 +693,7 @@ annotateActionsBlock[ art ]
681
693
  :
682
694
  ACTIONS { $art.actions = this.createDict(); } '{'
683
695
  ( annotateBoundAction[ $art ]
684
- ( ';' | <exitLoop> | <repeatLoop, cond=afterBrace> { this.noAssignmentInSameLine(); } )
696
+ ( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
685
697
  )*
686
698
  '}'<prepare=afterBrace>
687
699
  { this.finalizeExtensionsDict( $art.actions ); }
@@ -732,7 +744,7 @@ annotateElementsBlock[ art ]
732
744
  ( annotateElement[ $art ]
733
745
  ( ';'
734
746
  | <exitLoop>
735
- | <repeatLoop, cond=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
747
+ | <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
736
748
  )
737
749
  )*
738
750
  '}'<prepare=afterBrace>
@@ -755,7 +767,7 @@ extendElementsBlock[ art, start = undefined ]
755
767
  ( elementDefOrExtend[ $art ]
756
768
  ( ';'
757
769
  | <exitLoop>
758
- | <repeatLoop, cond=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); } )
770
+ | <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); } )
759
771
  )*
760
772
  '}'<prepare=afterBrace>
761
773
  { this.finalizeExtensionsDict( $art.elements ); }
@@ -838,7 +850,7 @@ typeOrIncludesSpec[ art ]
838
850
  // annotations are attached to the element, the type properties to the line type
839
851
  //
840
852
  // If used in a definition with additional clauses (currently just `= expr` for
841
- // elements), these clauses must be guarded with <cond=…>.
853
+ // elements), these clauses must be guarded with <guard=…>.
842
854
  //
843
855
  // This rule is for element, type, (input and `returns`) parameter and annotation
844
856
  // definitions. It is not used when the type expression is restricted: CDL-style
@@ -849,15 +861,15 @@ typeExpression[ art ]
849
861
  ( typeRefOptArgs[ $art ] | typeTypeOf[ $art ] )
850
862
  (<altRuleStart>)
851
863
  { this.docComment( $art ); } annoAssignStd[ $art ]*
852
- ( <cond=elementRestriction, arg=notNull> nullability[ $art ]
864
+ ( <guard=elementRestriction, arg=notNull> nullability[ $art ]
853
865
  { this.docComment( $art ); } annoAssignStd[ $art ]*
854
866
  )?
855
867
  ( enumSymbolsBlock[ $art ] <prepare=elementRestriction, arg=anno>
856
- ( <cond=elementRestriction, arg=notNull> nullability[ $art ] )?
857
- ( <cond=elementRestriction, arg=default>
868
+ ( <guard=elementRestriction, arg=notNull> nullability[ $art ] )?
869
+ ( <guard=elementRestriction, arg=default>
858
870
  DEFAULT expr=expression { $art.default = $expr; }
859
871
  )?
860
- ( <cond=elementRestriction, arg=notNull> nullability[ $art ] )?
872
+ ( <guard=elementRestriction, arg=notNull> nullability[ $art ] )?
861
873
  | typeProperties[ $art ]
862
874
  )?
863
875
  |
@@ -868,12 +880,12 @@ typeExpression[ art ]
868
880
  |
869
881
  assoc=ASSOCIATION <prepare=elementRestriction, arg=calc>
870
882
  cardinality[ $art ]? TO
871
- ( <cond=noRepeatedCardinality> card=ONE/MANY )?
883
+ ( <guard=noRepeatedCardinality> card=ONE/MANY )?
872
884
  typeAssocProperties[ $art, $assoc, $card ]
873
885
  |
874
886
  assoc=COMPOSITION <prepare=elementRestriction, arg=calc>
875
887
  cardinality[ $art ]? OF
876
- ( <cond=noRepeatedCardinality> card=ONE/MANY )?
888
+ ( <guard=noRepeatedCardinality> card=ONE/MANY )?
877
889
  ( typeAssocProperties[ $art, $assoc, $card ]
878
890
  | elementsBlock[ this.setAssocAndComposition( $art, $assoc, $card ) ]
879
891
  { $art.target.location = $art.target.elements[Symbol.for('cds.$location')]; }
@@ -886,7 +898,7 @@ typeExpression[ art ]
886
898
  ) // no anno assignments, except to end type expression
887
899
  (
888
900
  ( typeRefOptArgs[ $art.items ] | typeTypeOf[ $art.items ] )
889
- ( <cond=elementRestriction, arg=notNull> nullability[ $art.items ] )?
901
+ ( <guard=elementRestriction, arg=notNull> nullability[ $art.items ] )?
890
902
  ( { this.docComment( $art ); } annoAssignStd[ $art ]*
891
903
  { ; } <exitRule> // TODO TOOL: make it work without workaround { ; }
892
904
  // TODO TOOL: investigate why simply `{} <exitRule>` is ignored
@@ -895,7 +907,7 @@ typeExpression[ art ]
895
907
  |
896
908
  elementsBlock[ $art.items ]
897
909
  )
898
- ( <cond=elementRestriction, arg=notNull> nullability[ $art.items ] )?
910
+ ( <guard=elementRestriction, arg=notNull> nullability[ $art.items ] )?
899
911
  |
900
912
  elementsBlock[ $art ] <prepare=elementRestriction, arg=calc>
901
913
  nullability[ $art ]?
@@ -918,16 +930,17 @@ typeProperties[ art ]
918
930
  (
919
931
  annoAssignStd[ $art ]
920
932
  |
921
- <cond=elementRestriction, arg=notNull>
933
+ <guard=elementRestriction, arg=notNull>
922
934
  nullability[ $art ] { this.docComment( $art ); }
923
935
  |
924
- <cond=elementRestriction, arg=default>
936
+ <guard=elementRestriction, arg=default>
925
937
  DEFAULT expr=expression { $art.default = $expr; this.docComment( $art ); }
926
938
  )+
927
939
  ;
928
940
 
929
941
 
930
942
  typeTypeOf[ art ] locals[ location ]
943
+ @after{ this.attachLocation( $art.type ); }
931
944
  :
932
945
  TYPE OF { location = this.locationOfPrevTokens( 2 ); }
933
946
  type=simplePath[ 'ref' ] { $art.type = $type; }
@@ -954,6 +967,7 @@ typeRefOptArgs[ art ] locals[ type = $art.type ]
954
967
  (
955
968
  '.' Id_all['ref'] { $type.path.push( this.identAst() ); }
956
969
  )*
970
+ { this.attachLocation( $art.type ); }
957
971
  |
958
972
  open='('
959
973
  (
@@ -1058,12 +1072,11 @@ queryEOF returns[ query ]
1058
1072
  projectionSpec returns[ default query = {} ]
1059
1073
  @finally{ this.attachLocation($query); }
1060
1074
  :
1061
- // TODO, currently just with simple ref
1062
- PROJECTION <prepare=afterBrace> // v6: remove <prepare>
1075
+ PROJECTION
1076
+ <prepare=afterBrace, arg=entity> // enable special loop-exit, TODO v6 delete
1063
1077
  { $query = { op: this.valueWithLocation( 'SELECT' ) }; }
1064
1078
  ON
1065
- tab=fromRefWithOptAlias <prepare=afterBrace>
1066
- // TODO: this <prepare=afterBrace> is extremely strange... v6 forbid.
1079
+ tab=fromRefWithOptAlias
1067
1080
  { $query.from = tab; }
1068
1081
  selectItemsList[ $query ]?
1069
1082
  excludingClause[ $query ]?
@@ -1087,7 +1100,7 @@ queryExpression returns[ default expr = {} ] locals[ op, quantifier ]
1087
1100
  )
1088
1101
  query=queryExpression
1089
1102
  // with same op/quantifier: make left-assoc binary to nary:
1090
- { 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: ...$
1091
1104
  { $quantifier = undefined; }
1092
1105
  { $expr.args.push( $query ); this.attachLocation( $expr ); }
1093
1106
  )*
@@ -1180,16 +1193,16 @@ tableOrQueryParens returns[ default expr ]
1180
1193
  '(' <prepare=queryOnLeft>
1181
1194
  ( <prefer> tableOrQueryParens[ ...$ ]
1182
1195
  ( tableExpression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=table>
1183
- | <cond=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1196
+ | <guard=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1184
1197
  )?
1185
1198
  | tableExpression[ ...$ ] <prepare=queryOnLeft, arg=table>
1186
1199
  | queryExpression[ ...$ ]
1187
1200
  )
1188
1201
  ')'
1189
1202
  { this.surroundByParens( $expr ); }
1190
- ( <cond=queryOnLeft, arg=table> AS Id['FromAlias']
1203
+ ( <guard=queryOnLeft, arg=table> AS Id['FromAlias']
1191
1204
  { $expr = this.taggedIfQuery( $expr ); $expr.name = this.identAst(); }
1192
- | <cond=queryOnLeft, arg=tableWithoutAs> Id_restricted['FromAlias']
1205
+ | <guard=queryOnLeft, arg=tableWithoutAs> Id_restricted['FromAlias']
1193
1206
  // TODO TOOL: shouldn't we have generated `default: this.giR()`?
1194
1207
  { $expr = this.taggedIfQuery( $expr ); $expr.name = this.fragileAlias(); }
1195
1208
  )?
@@ -1228,7 +1241,7 @@ fromRefWithOptAlias returns[ default expr = {} ]
1228
1241
  (
1229
1242
  AS Id['FromAlias'] { $expr.name = this.identAst(); }
1230
1243
  |
1231
- // <cond=tableWithoutAs> not necessary, tool uses `default: this.giR()`
1244
+ // <guard=tableWithoutAs> not necessary, tool uses `default: this.giR()`
1232
1245
  Id_restricted['FromAlias']
1233
1246
  { $expr.name = this.fragileAlias(); }
1234
1247
  |
@@ -1243,7 +1256,7 @@ fromPath[ table, category ] locals[ pathItem ]
1243
1256
  Id[ $category ] { $table.path.push( $pathItem = this.identAst() ); }
1244
1257
  ( fromArgumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
1245
1258
  (
1246
- <cond=notAfterEntityArgOrFilter> // TODO TOOL: allow <hide=method>
1259
+ <guard=notAfterEntityArgOrFilter> // TODO TOOL: allow <hide=method>
1247
1260
  '.' { if (!$pathItem && !$table.scope) {
1248
1261
  $table.scope = $table.path.length; $category = 'ref';
1249
1262
  this.warning( 'syntax-invalid-path-separator', this.lb(),
@@ -1277,7 +1290,7 @@ fromNamedArgument[ pathStep ]
1277
1290
  cardinalityAndFilter[ pathStep ]
1278
1291
  :
1279
1292
  '['
1280
- ( <cond=beforeColon> Number // TODO: only allow `1`?
1293
+ ( <guard=beforeColon> Number // TODO: only allow `1`?
1281
1294
  { $pathStep.cardinality = { targetMax: this.unsignedIntegerLiteral(), location: this.lb().location }; }
1282
1295
  ':'
1283
1296
  )?
@@ -1341,7 +1354,7 @@ selectItemsList[ query, start = undefined ]
1341
1354
 
1342
1355
  nestedSelectItemsList[ query, clause ]
1343
1356
  :
1344
- '{'<prepare=inSelectItem, arg=nested>
1357
+ '{'<prepare=inSelectItem>
1345
1358
  { $query[$clause] = this.createArray(); }
1346
1359
  (
1347
1360
  ( '*' { $query[$clause].push( this.valueWithLocation() ); }
@@ -1353,43 +1366,72 @@ nestedSelectItemsList[ query, clause ]
1353
1366
  { this.finalizeDictOrArray( $query[$clause] ); }
1354
1367
  ;
1355
1368
 
1356
- selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
1369
+ selectItemDef[ columns ] locals[ art = new XsnArtifact() ]
1357
1370
  @finally{ this.attachLocation( $art ); }
1358
1371
  :
1359
1372
  { $columns.push( $art ); } // TODO: probably too early
1360
1373
  { this.docComment( $art ); } annoAssignCol[ $art ]*
1361
- ( <cond=modifierRestriction> VIRTUAL
1374
+ ( <guard=modifierRestriction> VIRTUAL
1362
1375
  { $art.virtual = this.valueWithLocation( true ); }
1363
1376
  )?
1364
1377
  {;} <prepare=columnExpr, arg=key> // TOOL TODO: disappears without {;}
1365
- ( <cond=modifierRestriction> KEY
1378
+ ( <guard=modifierRestriction> KEY
1366
1379
  { $art.key = this.valueWithLocation( true ); }
1367
1380
  )?
1381
+ // TODO: we might have an extra rule for column expression...
1368
1382
  (
1369
- expr=expression { $art.value = $expr; }
1370
- ( as=AS Id['ItemAlias'] { $art.name = this.identAst(); }
1371
- | Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
1372
- | { $alias = this.classifyImplicitName( 'ItemImplicit', $expr ); }
1383
+ (
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
- // TODO: guard for ref-only expression can probably replace reportExpandInline
1375
- '.'
1376
- { this.reportUnexpectedSpace( this.lb(), this.la().location, true ); } // TODO: no ERR
1377
- { this.reportExpandInline( $art, $as || true ); }
1378
- // no extra 'syntax-unexpected-alias' anymore,
1379
- // 'syntax-unexpected-anno' reported in define.js
1380
- { if ($alias) $alias.token.parsedAs = $alias.parsedAs; }
1389
+ <prefer>
1390
+ expr=valuePath { $expr = this.valuePathAst( $expr ); $art.value = $expr; }
1381
1391
  (
1382
- nestedSelectItemsList[ $art, 'inline' ]
1383
- 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(); }
1407
+ |
1408
+ Id_restricted['ItemAlias'] <prepare=nestedExpand>
1409
+ { $art.name = this.fragileAlias( true ); }
1384
1410
  |
1385
- '*' { $art.inline = [ this.valueWithLocation() ]; }
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>
1386
1427
  )
1387
- <exitRule>
1388
1428
  )
1389
- // TODO: <cond> instead `reportExpandInline` for "expand/inline only w/ ref"
1429
+ // expand is only allowed only after valuePath, but error recovery is poor
1430
+ // if the following is moved up to <prepare=nestedExpand>
1390
1431
  (
1391
- // TODO: guard for ref-only expression can probably replace:
1392
- { this.reportExpandInline( $art, false ); }
1432
+ // TODO: make guard handle that if valuePath might be function?
1433
+ <guard=nestedExpand>
1434
+ { if (!this.nestedExpand(true)) this.reportExpandInline( $art, false ); }
1393
1435
  nestedSelectItemsList[ $art, 'expand' ]
1394
1436
  excludingClause[ $art ]?
1395
1437
  )?
@@ -1400,7 +1442,7 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
1400
1442
  )
1401
1443
  { this.docComment( $art ); } <prepare=columnExpr> annoAssignMid[ $art ]*
1402
1444
  (
1403
- ':'
1445
+ ':' // TODO: guard? currently not with expand ?
1404
1446
  (
1405
1447
  ( typeTypeOf[ $art ]
1406
1448
  | ( LOCALIZED { $art.localized = this.valueWithLocation( true ); } )?
@@ -1413,14 +1455,14 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
1413
1455
  | foreignKeysBlock[ $art ]
1414
1456
  )?
1415
1457
  |
1416
- ( <cond=columnExpr> // arg=singleId
1458
+ ( <guard=columnExpr> // arg=singleId
1417
1459
  assoc=ASSOCIATION { this.associationInSelectItem( $art ); }
1418
1460
  cardinality[ $art ]? TO
1419
- | <cond=columnExpr> // arg=singleId
1461
+ | <guard=columnExpr> // arg=singleId
1420
1462
  assoc=COMPOSITION { this.associationInSelectItem( $art ); }
1421
1463
  cardinality[ $art ]? OF
1422
1464
  )
1423
- ( <cond=noRepeatedCardinality> card=ONE/MANY )?
1465
+ ( <guard=noRepeatedCardinality> card=ONE/MANY )?
1424
1466
  target=simplePath
1425
1467
  { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
1426
1468
  ON expr=condition { $art.on = $expr; }
@@ -1485,7 +1527,7 @@ valuePath returns[ default expr = { path: [] } ] locals[ pathItem ]
1485
1527
  ( argumentsAndFilter[ $pathItem ] )?
1486
1528
  (<altRuleStart>)
1487
1529
  (
1488
- <cond=isDotForPath> '.'
1530
+ <guard=isDotForPath> '.'
1489
1531
  Id_all['ref'] { $expr.path.push( $pathItem = this.identAst() ); }
1490
1532
  ( argumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
1491
1533
  )*
@@ -1571,7 +1613,7 @@ expression returns[ default expr = {} ]
1571
1613
  | <prec=10, postfix=once> IS { $expr = this.applyOpToken( $expr ); }
1572
1614
  ( NOT { this.pushXprToken( $expr ); } )?
1573
1615
  NULL { this.pushXprToken( $expr ); }
1574
- | ( <arg=10, cond=isNegatedRelation> NOT { $expr = this.applyOpToken( $expr ); }
1616
+ | ( <arg=10, guard=isNegatedRelation> NOT { $expr = this.applyOpToken( $expr ); }
1575
1617
  // TODO: condition, because there might be NOT NULL after DEFAULT expression
1576
1618
  | <prec=10, postfix=once>
1577
1619
  { $expr = { op: { val: 'ixpr', location: this.la().location }, args: [ $expr ] }; }
@@ -1601,7 +1643,7 @@ expressionOrQueryParens returns[ default expr ]
1601
1643
  ( expression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=expr>
1602
1644
  continueExpressionslist[ ...$ ]?
1603
1645
  | continueExpressionslist[ ...$ ] <prepare=queryOnLeft, arg=expr>
1604
- | <cond=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1646
+ | <guard=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1605
1647
  )?
1606
1648
  | expression[ ...$ ] <prepare=queryOnLeft, arg=expr>
1607
1649
  continueExpressionslist[ ...$ ]?
@@ -1688,7 +1730,7 @@ options{ minTokensMatched = 1 }
1688
1730
  // TODO: if we want perfect CC and error recovery, `isNamedArg` would work
1689
1731
  // like keyword prediction and also do something similar to the (future)
1690
1732
  // "identifier confirmation prediction" (consider argument `(par ‡ : 42)`
1691
- <cond=isNamedArg> id=Id_all['paramname']
1733
+ <guard=isNamedArg> id=Id_all['paramname']
1692
1734
  (
1693
1735
  ':' { $pathStep.args = this.createDict( $open ); $pathStep.$syntax = ':'; }
1694
1736
  expr=expression { this.addNamedArg( $pathStep, $id, $expr ); }
@@ -1829,7 +1871,7 @@ literalValue returns[ default expr = {} ]
1829
1871
  :
1830
1872
  // TODO: remove from this rule (not in enum! `String enum { foo = #bar }`) ?
1831
1873
  '#' { this.reportUnexpectedSpace(); }
1832
- Id['enumref']
1874
+ Id_all['enumref']
1833
1875
  { $expr = { literal: 'enum', sym: this.identAst() } }
1834
1876
  |
1835
1877
  NULL
@@ -1887,6 +1929,7 @@ annoAssignParen[ art ]
1887
1929
  :
1888
1930
  '('<prepare=annoInSameLine>
1889
1931
  ( annoAssignBase[ $art ]
1932
+ //( annoAssignErrorRecoveryHelper )?
1890
1933
  ( ',' | <exitLoop> )
1891
1934
  )*
1892
1935
  ')'
@@ -1896,7 +1939,7 @@ annoAssignBase[ art ] locals[ value = {} ]
1896
1939
  @finally{ this.assignAnnotation( $art, $value, $name || {} ); }
1897
1940
  :
1898
1941
  name=annoNamePath
1899
- ( <cond=annoInSameLine> ':' value=annoValue )?
1942
+ ( <guard=annoInSameLine> ':' value=annoValue )?
1900
1943
  ;
1901
1944
 
1902
1945
  annoNamePath[ category = 'anno' ] returns[ default name = new XsnName() ]
@@ -1911,7 +1954,7 @@ annoNamePath[ category = 'anno' ] returns[ default name = new XsnName() ]
1911
1954
  Id_all[ $category ] { $name.path.push( this.identAstWithPrefix( $at ) ); }
1912
1955
  )
1913
1956
  )*
1914
- ( <cond=annoInSameLine> annoPathVariant[ $name ] )?
1957
+ ( <guard=annoInSameLine> annoPathVariant[ $name ] )?
1915
1958
  ;
1916
1959
 
1917
1960
  annoPath[ nameOrRef, category = 'annoref' ]
@@ -1969,17 +2012,18 @@ annoValue returns[ default value = {} ]
1969
2012
  }
1970
2013
  (
1971
2014
  // TODO: complain about empty loop if top-level as before
1972
- // TOOL TODO → allow `<cond=…> '}'` below where the condition rejects `}`
2015
+ // TOOL TODO → allow `<guard=…> '}'` below where the condition rejects `}`
1973
2016
  // after `{` if top-level
1974
2017
  sub=annoStructValue
1975
2018
  {
1976
2019
  if ($value.$flatten) $value.$flatten.push( $sub );
1977
2020
  else this.addDef( $sub, $value, 'struct', null, $sub.name );
1978
2021
  }
1979
- ( ',' | <exitLoop> )
2022
+ ( ',' | <exitLoop> | <guard=fail, repeatLoop, restrict=Id> )
2023
+ // <guard=fail, repeatLoop>` for better error recovery for input `foo@bar`
1980
2024
  )*
1981
2025
  // TODO TOOL: allow:
1982
- // ( <cond=arrayAnno, …> '}' | <error> ) // TODO TOOL - workaround:
2026
+ // ( <guard=arrayAnno, …> '}' | <error> ) // TODO TOOL - workaround:
1983
2027
  { this.ec( 'arrayAnno', 'orNotEmpty' ); } '}'
1984
2028
  // Do NOT use <prepare=afterBrace> here!
1985
2029
  |
@@ -1989,7 +2033,7 @@ annoValue returns[ default value = {} ]
1989
2033
  (
1990
2034
  ( sub=annoValue { $value.val.push( $sub ) }
1991
2035
  |
1992
- <cond=arrayAnno, arg=ellipsis> ellipsis='...'
2036
+ <guard=arrayAnno, arg=ellipsis> ellipsis='...'
1993
2037
  ( UP TO upTo=annoValue
1994
2038
  { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
1995
2039
  | { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location } ); }
@@ -2001,7 +2045,7 @@ annoValue returns[ default value = {} ]
2001
2045
  )
2002
2046
  ( ',' | <exitLoop> )
2003
2047
  )*
2004
- // TODO TOOL: allow ( <cond=arrayAnno, arg=bracket> ']' )
2048
+ // TODO TOOL: allow ( <guard=arrayAnno, arg=bracket> ']' )
2005
2049
  { this.ec( 'arrayAnno', 'bracket' ); }<always>
2006
2050
  ']'
2007
2051
  |
@@ -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