@abapify/acds 0.4.14 → 0.4.16

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.
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { CstParser, Lexer, createToken } from "chevrotain";
1
+ import { CstParser, EOF, Lexer, createToken } from "chevrotain";
2
2
 
3
3
  //#region src/tokens.ts
4
4
  /**
@@ -22,6 +22,18 @@ const BlockComment = createToken({
22
22
  pattern: /\/\*[\s\S]*?\*\//,
23
23
  group: Lexer.SKIPPED
24
24
  });
25
+ /**
26
+ * SQL-style line comment: `-- some text`.
27
+ *
28
+ * ABAP CDS sources (especially the classic `define view` ones) use SQL
29
+ * comments. The pattern is tried before the `Minus` token, so `--` always
30
+ * wins over two minus signs.
31
+ */
32
+ const SqlLineComment = createToken({
33
+ name: "SqlLineComment",
34
+ pattern: /--[^\n]*/,
35
+ group: Lexer.SKIPPED
36
+ });
25
37
  const StringLiteral = createToken({
26
38
  name: "StringLiteral",
27
39
  pattern: /'(?:[^'\\]|\\.)*'/
@@ -41,198 +53,684 @@ const Identifier = createToken({
41
53
  });
42
54
  const Define = createToken({
43
55
  name: "Define",
44
- pattern: /define/,
56
+ pattern: /define/i,
45
57
  longer_alt: Identifier
46
58
  });
47
59
  const Table = createToken({
48
60
  name: "Table",
49
- pattern: /table/,
61
+ pattern: /table/i,
50
62
  longer_alt: Identifier
51
63
  });
52
64
  const Structure = createToken({
53
65
  name: "Structure",
54
- pattern: /structure/,
66
+ pattern: /structure/i,
55
67
  longer_alt: Identifier
56
68
  });
57
69
  const Type = createToken({
58
70
  name: "Type",
59
- pattern: /type/,
71
+ pattern: /type/i,
60
72
  longer_alt: Identifier
61
73
  });
62
74
  const Service = createToken({
63
75
  name: "Service",
64
- pattern: /service/,
76
+ pattern: /service/i,
65
77
  longer_alt: Identifier
66
78
  });
67
79
  const Expose = createToken({
68
80
  name: "Expose",
69
- pattern: /expose/,
81
+ pattern: /expose/i,
70
82
  longer_alt: Identifier
71
83
  });
72
84
  const Annotate = createToken({
73
85
  name: "Annotate",
74
- pattern: /annotate/,
86
+ pattern: /annotate/i,
75
87
  longer_alt: Identifier
76
88
  });
77
89
  const Entity = createToken({
78
90
  name: "Entity",
79
- pattern: /entity/,
91
+ pattern: /entity/i,
80
92
  longer_alt: Identifier
81
93
  });
82
94
  const With = createToken({
83
95
  name: "With",
84
- pattern: /with/,
96
+ pattern: /with/i,
85
97
  longer_alt: Identifier
86
98
  });
87
99
  const Key = createToken({
88
100
  name: "Key",
89
- pattern: /key/,
101
+ pattern: /key/i,
90
102
  longer_alt: Identifier
91
103
  });
92
104
  const Not = createToken({
93
105
  name: "Not",
94
- pattern: /not/,
106
+ pattern: /not/i,
95
107
  longer_alt: Identifier
96
108
  });
97
109
  const Null = createToken({
98
110
  name: "Null",
99
- pattern: /null/,
111
+ pattern: /null/i,
100
112
  longer_alt: Identifier
101
113
  });
102
114
  const As = createToken({
103
115
  name: "As",
104
- pattern: /as/,
116
+ pattern: /as/i,
105
117
  longer_alt: Identifier
106
118
  });
107
119
  const Include = createToken({
108
120
  name: "Include",
109
- pattern: /include/,
121
+ pattern: /include/i,
110
122
  longer_alt: Identifier
111
123
  });
112
124
  const Suffix = createToken({
113
125
  name: "Suffix",
114
- pattern: /suffix/,
126
+ pattern: /suffix/i,
115
127
  longer_alt: Identifier
116
128
  });
117
129
  const True = createToken({
118
130
  name: "True",
119
- pattern: /true/,
131
+ pattern: /true/i,
120
132
  longer_alt: Identifier
121
133
  });
122
134
  const False = createToken({
123
135
  name: "False",
124
- pattern: /false/,
136
+ pattern: /false/i,
125
137
  longer_alt: Identifier
126
138
  });
127
139
  const Abap = createToken({
128
140
  name: "Abap",
129
- pattern: /abap/,
141
+ pattern: /abap/i,
130
142
  longer_alt: Identifier
131
143
  });
132
144
  const View = createToken({
133
145
  name: "View",
134
- pattern: /view/,
146
+ pattern: /view/i,
135
147
  longer_alt: Identifier
136
148
  });
137
149
  const Projection = createToken({
138
150
  name: "Projection",
139
- pattern: /projection/,
151
+ pattern: /projection/i,
140
152
  longer_alt: Identifier
141
153
  });
142
154
  const Select = createToken({
143
155
  name: "Select",
144
- pattern: /select/,
156
+ pattern: /select/i,
145
157
  longer_alt: Identifier
146
158
  });
147
159
  const From = createToken({
148
160
  name: "From",
149
- pattern: /from/,
161
+ pattern: /from/i,
150
162
  longer_alt: Identifier
151
163
  });
152
164
  const On = createToken({
153
165
  name: "On",
154
- pattern: /on/,
166
+ pattern: /on/i,
155
167
  longer_alt: Identifier
156
168
  });
157
169
  const Association = createToken({
158
170
  name: "Association",
159
- pattern: /association/,
171
+ pattern: /association/i,
160
172
  longer_alt: Identifier
161
173
  });
162
174
  const Composition = createToken({
163
175
  name: "Composition",
164
- pattern: /composition/,
176
+ pattern: /composition/i,
165
177
  longer_alt: Identifier
166
178
  });
167
179
  const To = createToken({
168
180
  name: "To",
169
- pattern: /to/,
181
+ pattern: /to/i,
170
182
  longer_alt: Identifier
171
183
  });
172
184
  const Many = createToken({
173
185
  name: "Many",
174
- pattern: /many/,
186
+ pattern: /many/i,
175
187
  longer_alt: Identifier
176
188
  });
177
189
  const One = createToken({
178
190
  name: "One",
179
- pattern: /one/,
191
+ pattern: /one/i,
180
192
  longer_alt: Identifier
181
193
  });
182
194
  const Of = createToken({
183
195
  name: "Of",
184
- pattern: /of/,
196
+ pattern: /of/i,
185
197
  longer_alt: Identifier
186
198
  });
187
199
  const Parameters = createToken({
188
200
  name: "Parameters",
189
- pattern: /parameters/,
201
+ pattern: /parameters/i,
190
202
  longer_alt: Identifier
191
203
  });
192
204
  const Role = createToken({
193
205
  name: "Role",
194
- pattern: /role/,
206
+ pattern: /role/i,
195
207
  longer_alt: Identifier
196
208
  });
197
209
  const Grant = createToken({
198
210
  name: "Grant",
199
- pattern: /grant/,
211
+ pattern: /grant/i,
200
212
  longer_alt: Identifier
201
213
  });
202
214
  const Where = createToken({
203
215
  name: "Where",
204
- pattern: /where/,
216
+ pattern: /where/i,
205
217
  longer_alt: Identifier
206
218
  });
207
219
  const Abstract = createToken({
208
220
  name: "Abstract",
209
- pattern: /abstract/,
221
+ pattern: /abstract/i,
210
222
  longer_alt: Identifier
211
223
  });
212
224
  const Custom = createToken({
213
225
  name: "Custom",
214
- pattern: /custom/,
226
+ pattern: /custom/i,
215
227
  longer_alt: Identifier
216
228
  });
217
229
  const Virtual = createToken({
218
230
  name: "Virtual",
219
- pattern: /virtual/,
231
+ pattern: /virtual/i,
220
232
  longer_alt: Identifier
221
233
  });
222
234
  const Redirected = createToken({
223
235
  name: "Redirected",
224
- pattern: /redirected/,
236
+ pattern: /redirected/i,
225
237
  longer_alt: Identifier
226
238
  });
227
239
  const Default = createToken({
228
240
  name: "Default",
229
- pattern: /default/,
241
+ pattern: /default/i,
242
+ longer_alt: Identifier
243
+ });
244
+ const Join = createToken({
245
+ name: "Join",
246
+ pattern: /join/i,
247
+ longer_alt: Identifier
248
+ });
249
+ const Inner = createToken({
250
+ name: "Inner",
251
+ pattern: /inner/i,
252
+ longer_alt: Identifier
253
+ });
254
+ const Left = createToken({
255
+ name: "Left",
256
+ pattern: /left/i,
257
+ longer_alt: Identifier
258
+ });
259
+ const Right = createToken({
260
+ name: "Right",
261
+ pattern: /right/i,
262
+ longer_alt: Identifier
263
+ });
264
+ const Cross = createToken({
265
+ name: "Cross",
266
+ pattern: /cross/i,
267
+ longer_alt: Identifier
268
+ });
269
+ const Full = createToken({
270
+ name: "Full",
271
+ pattern: /full/i,
272
+ longer_alt: Identifier
273
+ });
274
+ /**
275
+ * `OUTER` — must be listed BEFORE `Or`, because `or` is a prefix of `outer`
276
+ * and the lexer resolves the first matching token in list order.
277
+ */
278
+ const Outer = createToken({
279
+ name: "Outer",
280
+ pattern: /outer/i,
281
+ longer_alt: Identifier
282
+ });
283
+ const Distinct = createToken({
284
+ name: "Distinct",
285
+ pattern: /distinct/i,
286
+ longer_alt: Identifier
287
+ });
288
+ const Union = createToken({
289
+ name: "Union",
290
+ pattern: /union/i,
291
+ longer_alt: Identifier
292
+ });
293
+ const Intersect = createToken({
294
+ name: "Intersect",
295
+ pattern: /intersect/i,
296
+ longer_alt: Identifier
297
+ });
298
+ const Except = createToken({
299
+ name: "Except",
300
+ pattern: /except/i,
301
+ longer_alt: Identifier
302
+ });
303
+ const Allowed = createToken({
304
+ name: "Allowed",
305
+ pattern: /allowed/i,
306
+ longer_alt: Identifier
307
+ });
308
+ /** NOTE: `allowed` must precede `all` (`all` is a prefix of `allowed`). */
309
+ const All = createToken({
310
+ name: "All",
311
+ pattern: /all/i,
312
+ longer_alt: Identifier
313
+ });
314
+ const Group = createToken({
315
+ name: "Group",
316
+ pattern: /group/i,
317
+ longer_alt: Identifier
318
+ });
319
+ const Having = createToken({
320
+ name: "Having",
321
+ pattern: /having/i,
322
+ longer_alt: Identifier
323
+ });
324
+ const Root = createToken({
325
+ name: "Root",
326
+ pattern: /root/i,
327
+ longer_alt: Identifier
328
+ });
329
+ const Writable = createToken({
330
+ name: "Writable",
331
+ pattern: /writable/i,
332
+ longer_alt: Identifier
333
+ });
334
+ const Switchable = createToken({
335
+ name: "Switchable",
336
+ pattern: /switchable/i,
337
+ longer_alt: Identifier
338
+ });
339
+ const Exact = createToken({
340
+ name: "Exact",
341
+ pattern: /exact/i,
342
+ longer_alt: Identifier
343
+ });
344
+ const Parents = createToken({
345
+ name: "Parents",
346
+ pattern: /parents/i,
347
+ longer_alt: Identifier
348
+ });
349
+ const Parent = createToken({
350
+ name: "Parent",
351
+ pattern: /parent/i,
352
+ longer_alt: Identifier
353
+ });
354
+ const Child = createToken({
355
+ name: "Child",
356
+ pattern: /child/i,
357
+ longer_alt: Identifier
358
+ });
359
+ const Localized = createToken({
360
+ name: "Localized",
361
+ pattern: /localized/i,
362
+ longer_alt: Identifier
363
+ });
364
+ const Redefine = createToken({
365
+ name: "Redefine",
366
+ pattern: /redefine/i,
367
+ longer_alt: Identifier
368
+ });
369
+ const Redefinition = createToken({
370
+ name: "Redefinition",
371
+ pattern: /redefinition/i,
372
+ longer_alt: Identifier
373
+ });
374
+ const Filter = createToken({
375
+ name: "Filter",
376
+ pattern: /filter/i,
377
+ longer_alt: Identifier
378
+ });
379
+ const Bind = createToken({
380
+ name: "Bind",
381
+ pattern: /bind/i,
382
+ longer_alt: Identifier
383
+ });
384
+ const FunctionKeyword = createToken({
385
+ name: "Function",
386
+ pattern: /function/i,
387
+ longer_alt: Identifier
388
+ });
389
+ const Returns = createToken({
390
+ name: "Returns",
391
+ pattern: /returns/i,
392
+ longer_alt: Identifier
393
+ });
394
+ const Implemented = createToken({
395
+ name: "Implemented",
396
+ pattern: /implemented/i,
397
+ longer_alt: Identifier
398
+ });
399
+ const Method = createToken({
400
+ name: "Method",
401
+ pattern: /method/i,
402
+ longer_alt: Identifier
403
+ });
404
+ const Scalar = createToken({
405
+ name: "Scalar",
406
+ pattern: /scalar/i,
407
+ longer_alt: Identifier
408
+ });
409
+ const Hierarchy = createToken({
410
+ name: "Hierarchy",
411
+ pattern: /hierarchy/i,
412
+ longer_alt: Identifier
413
+ });
414
+ const Aspect = createToken({
415
+ name: "Aspect",
416
+ pattern: /aspect/i,
417
+ longer_alt: Identifier
418
+ });
419
+ const AnnotationKeyword = createToken({
420
+ name: "AnnotationKeyword",
421
+ pattern: /annotation/i,
422
+ longer_alt: Identifier
423
+ });
424
+ const Extend = createToken({
425
+ name: "Extend",
426
+ pattern: /extend/i,
427
+ longer_alt: Identifier
428
+ });
429
+ const Accesspolicy = createToken({
430
+ name: "Accesspolicy",
431
+ pattern: /accesspolicy/i,
432
+ longer_alt: Identifier
433
+ });
434
+ const External = createToken({
435
+ name: "External",
436
+ pattern: /external/i,
437
+ longer_alt: Identifier
438
+ });
439
+ const Provider = createToken({
440
+ name: "Provider",
441
+ pattern: /provider/i,
442
+ longer_alt: Identifier
443
+ });
444
+ const Contract = createToken({
445
+ name: "Contract",
446
+ pattern: /contract/i,
447
+ longer_alt: Identifier
448
+ });
449
+ const Name = createToken({
450
+ name: "Name",
451
+ pattern: /name/i,
452
+ longer_alt: Identifier
453
+ });
454
+ const Federated = createToken({
455
+ name: "Federated",
456
+ pattern: /federated/i,
457
+ longer_alt: Identifier
458
+ });
459
+ const Data = createToken({
460
+ name: "Data",
461
+ pattern: /data/i,
462
+ longer_alt: Identifier
463
+ });
464
+ const Provided = createToken({
465
+ name: "Provided",
466
+ pattern: /provided/i,
467
+ longer_alt: Identifier
468
+ });
469
+ const AtKeyword = createToken({
470
+ name: "AtKeyword",
471
+ pattern: /at/i,
472
+ longer_alt: Identifier
473
+ });
474
+ const Runtime = createToken({
475
+ name: "Runtime",
476
+ pattern: /runtime/i,
477
+ longer_alt: Identifier
478
+ });
479
+ const Case = createToken({
480
+ name: "Case",
481
+ pattern: /case/i,
482
+ longer_alt: Identifier
483
+ });
484
+ const When = createToken({
485
+ name: "When",
486
+ pattern: /when/i,
487
+ longer_alt: Identifier
488
+ });
489
+ const Then = createToken({
490
+ name: "Then",
491
+ pattern: /then/i,
492
+ longer_alt: Identifier
493
+ });
494
+ const Else = createToken({
495
+ name: "Else",
496
+ pattern: /else/i,
497
+ longer_alt: Identifier
498
+ });
499
+ const End = createToken({
500
+ name: "End",
501
+ pattern: /end/i,
502
+ longer_alt: Identifier
503
+ });
504
+ const Like = createToken({
505
+ name: "Like",
506
+ pattern: /like/i,
507
+ longer_alt: Identifier
508
+ });
509
+ const Between = createToken({
510
+ name: "Between",
511
+ pattern: /between/i,
512
+ longer_alt: Identifier
513
+ });
514
+ const Over = createToken({
515
+ name: "Over",
516
+ pattern: /over/i,
517
+ longer_alt: Identifier
518
+ });
519
+ const Partition = createToken({
520
+ name: "Partition",
521
+ pattern: /partition/i,
522
+ longer_alt: Identifier
523
+ });
524
+ const Siblings = createToken({
525
+ name: "Siblings",
526
+ pattern: /siblings/i,
527
+ longer_alt: Identifier
528
+ });
529
+ const Order = createToken({
530
+ name: "Order",
531
+ pattern: /order/i,
532
+ longer_alt: Identifier
533
+ });
534
+ const Start = createToken({
535
+ name: "Start",
536
+ pattern: /start/i,
537
+ longer_alt: Identifier
538
+ });
539
+ const Source = createToken({
540
+ name: "Source",
541
+ pattern: /source/i,
542
+ longer_alt: Identifier
543
+ });
544
+ const Directory = createToken({
545
+ name: "Directory",
546
+ pattern: /directory/i,
547
+ longer_alt: Identifier
548
+ });
549
+ const Period = createToken({
550
+ name: "Period",
551
+ pattern: /period/i,
552
+ longer_alt: Identifier
553
+ });
554
+ const Valid = createToken({
555
+ name: "Valid",
556
+ pattern: /valid/i,
557
+ longer_alt: Identifier
558
+ });
559
+ const Depth = createToken({
560
+ name: "Depth",
561
+ pattern: /depth/i,
562
+ longer_alt: Identifier
563
+ });
564
+ const Nodetype = createToken({
565
+ name: "Nodetype",
566
+ pattern: /nodetype/i,
567
+ longer_alt: Identifier
568
+ });
569
+ const Load = createToken({
570
+ name: "Load",
571
+ pattern: /load/i,
572
+ longer_alt: Identifier
573
+ });
574
+ const Multiple = createToken({
575
+ name: "Multiple",
576
+ pattern: /multiple/i,
577
+ longer_alt: Identifier
578
+ });
579
+ const Orphans = createToken({
580
+ name: "Orphans",
581
+ pattern: /orphans/i,
582
+ longer_alt: Identifier
583
+ });
584
+ const Cycles = createToken({
585
+ name: "Cycles",
586
+ pattern: /cycles/i,
587
+ longer_alt: Identifier
588
+ });
589
+ const Generate = createToken({
590
+ name: "Generate",
591
+ pattern: /generate/i,
592
+ longer_alt: Identifier
593
+ });
594
+ const Spantree = createToken({
595
+ name: "Spantree",
596
+ pattern: /spantree/i,
597
+ longer_alt: Identifier
598
+ });
599
+ const Cache = createToken({
600
+ name: "Cache",
601
+ pattern: /cache/i,
602
+ longer_alt: Identifier
603
+ });
604
+ const Bulk = createToken({
605
+ name: "Bulk",
606
+ pattern: /bulk/i,
607
+ longer_alt: Identifier
608
+ });
609
+ const Incremental = createToken({
610
+ name: "Incremental",
611
+ pattern: /incremental/i,
612
+ longer_alt: Identifier
613
+ });
614
+ const Leaves = createToken({
615
+ name: "Leaves",
616
+ pattern: /leaves/i,
617
+ longer_alt: Identifier
618
+ });
619
+ const Only = createToken({
620
+ name: "Only",
621
+ pattern: /only/i,
622
+ longer_alt: Identifier
623
+ });
624
+ const Ascending = createToken({
625
+ name: "Ascending",
626
+ pattern: /ascending/i,
627
+ longer_alt: Identifier
628
+ });
629
+ const Descending = createToken({
630
+ name: "Descending",
631
+ pattern: /descending/i,
632
+ longer_alt: Identifier
633
+ });
634
+ const ErrorKeyword = createToken({
635
+ name: "Error",
636
+ pattern: /error/i,
637
+ longer_alt: Identifier
638
+ });
639
+ const Ignore = createToken({
640
+ name: "Ignore",
641
+ pattern: /ignore/i,
642
+ longer_alt: Identifier
643
+ });
644
+ const Breakup = createToken({
645
+ name: "Breakup",
646
+ pattern: /breakup/i,
647
+ longer_alt: Identifier
648
+ });
649
+ const Force = createToken({
650
+ name: "Force",
651
+ pattern: /force/i,
652
+ longer_alt: Identifier
653
+ });
654
+ /** `FOR` — must be listed AFTER `Force` (`for` is a prefix of `force`). */
655
+ const For = createToken({
656
+ name: "For",
657
+ pattern: /for/i,
658
+ longer_alt: Identifier
659
+ });
660
+ const By = createToken({
661
+ name: "By",
662
+ pattern: /by/i,
663
+ longer_alt: Identifier
664
+ });
665
+ /** `OFF` — must be listed BEFORE `Of` (`of` is a prefix of `off`). */
666
+ const Off = createToken({
667
+ name: "Off",
668
+ pattern: /off/i,
669
+ longer_alt: Identifier
670
+ });
671
+ const Inheriting = createToken({
672
+ name: "Inheriting",
673
+ pattern: /inheriting/i,
674
+ longer_alt: Identifier
675
+ });
676
+ const Inherit = createToken({
677
+ name: "Inherit",
678
+ pattern: /inherit/i,
679
+ longer_alt: Identifier
680
+ });
681
+ const Conditions = createToken({
682
+ name: "Conditions",
683
+ pattern: /conditions/i,
684
+ longer_alt: Identifier
685
+ });
686
+ const Super = createToken({
687
+ name: "Super",
688
+ pattern: /super/i,
689
+ longer_alt: Identifier
690
+ });
691
+ const Void = createToken({
692
+ name: "Void",
693
+ pattern: /void/i,
694
+ longer_alt: Identifier
695
+ });
696
+ const Combination = createToken({
697
+ name: "Combination",
698
+ pattern: /combination/i,
699
+ longer_alt: Identifier
700
+ });
701
+ const Mode = createToken({
702
+ name: "Mode",
703
+ pattern: /mode/i,
704
+ longer_alt: Identifier
705
+ });
706
+ const Optional = createToken({
707
+ name: "Optional",
708
+ pattern: /optional/i,
709
+ longer_alt: Identifier
710
+ });
711
+ const Elements = createToken({
712
+ name: "Elements",
713
+ pattern: /elements/i,
714
+ longer_alt: Identifier
715
+ });
716
+ /**
717
+ * `IN` — must be listed AFTER every `in*` keyword (`inner`, `intersect`,
718
+ * `include`, `inherit`, `inheriting`, `incremental`), because `in` is a
719
+ * prefix of all of them.
720
+ */
721
+ const In = createToken({
722
+ name: "In",
723
+ pattern: /in/i,
230
724
  longer_alt: Identifier
231
725
  });
232
726
  const EqEq = createToken({
233
727
  name: "EqEq",
234
728
  pattern: /==/
235
729
  });
730
+ const FatArrow = createToken({
731
+ name: "FatArrow",
732
+ pattern: /=>/
733
+ });
236
734
  const Eq = createToken({
237
735
  name: "Eq",
238
736
  pattern: /=/
@@ -257,14 +755,18 @@ const Gt = createToken({
257
755
  name: "Gt",
258
756
  pattern: />/
259
757
  });
758
+ const Quest = createToken({
759
+ name: "Quest",
760
+ pattern: /\?/
761
+ });
260
762
  const And = createToken({
261
763
  name: "And",
262
- pattern: /and/,
764
+ pattern: /and/i,
263
765
  longer_alt: Identifier
264
766
  });
265
767
  const Or = createToken({
266
768
  name: "Or",
267
- pattern: /or/,
769
+ pattern: /or/i,
268
770
  longer_alt: Identifier
269
771
  });
270
772
  const At = createToken({
@@ -275,6 +777,11 @@ const Colon = createToken({
275
777
  name: "Colon",
276
778
  pattern: /:/
277
779
  });
780
+ /** `$` — prefix of system variables such as `$session`, `$parameters`, `$projection`. */
781
+ const Dollar = createToken({
782
+ name: "Dollar",
783
+ pattern: /\$/
784
+ });
278
785
  const Semicolon = createToken({
279
786
  name: "Semicolon",
280
787
  pattern: /;/
@@ -327,17 +834,21 @@ const Slash = createToken({
327
834
  name: "Slash",
328
835
  pattern: /\//
329
836
  });
330
- const allTokens = [
331
- WhiteSpace,
332
- LineComment,
333
- BlockComment,
334
- StringLiteral,
335
- NumberLiteral,
336
- EnumLiteral,
837
+ /**
838
+ * All keyword tokens. Exported so the parser can derive its name-token set
839
+ * (every keyword may legally appear as an identifier in CDS).
840
+ */
841
+ const keywordTokens = [
337
842
  Association,
338
843
  Composition,
339
844
  Abstract,
340
845
  Annotate,
846
+ AnnotationKeyword,
847
+ Accesspolicy,
848
+ Ascending,
849
+ Aspect,
850
+ Allowed,
851
+ As,
341
852
  Define,
342
853
  Table,
343
854
  Structure,
@@ -349,13 +860,13 @@ const allTokens = [
349
860
  Key,
350
861
  Not,
351
862
  Null,
352
- As,
353
863
  Include,
354
864
  Suffix,
355
865
  True,
356
866
  False,
357
867
  Abap,
358
868
  One,
869
+ Only,
359
870
  View,
360
871
  Projection,
361
872
  Select,
@@ -363,6 +874,7 @@ const allTokens = [
363
874
  On,
364
875
  To,
365
876
  Many,
877
+ Off,
366
878
  Of,
367
879
  Parameters,
368
880
  Role,
@@ -371,16 +883,115 @@ const allTokens = [
371
883
  Custom,
372
884
  Virtual,
373
885
  Redirected,
886
+ Redefinition,
887
+ Redefine,
374
888
  Default,
889
+ Distinct,
890
+ Union,
891
+ Intersect,
892
+ Except,
893
+ All,
894
+ Group,
895
+ Having,
896
+ Root,
897
+ Writable,
898
+ Switchable,
899
+ Exact,
900
+ Parents,
901
+ Parent,
902
+ Child,
903
+ Localized,
904
+ Filter,
905
+ Bind,
906
+ FunctionKeyword,
907
+ Returns,
908
+ Implemented,
909
+ Method,
910
+ Scalar,
911
+ Hierarchy,
912
+ Extend,
913
+ External,
914
+ Provider,
915
+ Provided,
916
+ Contract,
917
+ Name,
918
+ Federated,
919
+ Data,
920
+ AtKeyword,
921
+ Runtime,
922
+ Case,
923
+ When,
924
+ Then,
925
+ Else,
926
+ End,
927
+ Like,
928
+ Between,
929
+ Over,
930
+ Partition,
931
+ Siblings,
932
+ Order,
933
+ Orphans,
934
+ Optional,
935
+ Start,
936
+ Source,
937
+ Directory,
938
+ Period,
939
+ Valid,
940
+ Depth,
941
+ Nodetype,
942
+ Load,
943
+ Multiple,
944
+ Cycles,
945
+ Generate,
946
+ Spantree,
947
+ Cache,
948
+ Bulk,
949
+ Incremental,
950
+ Leaves,
951
+ Descending,
952
+ ErrorKeyword,
953
+ Ignore,
954
+ Breakup,
955
+ Force,
956
+ For,
957
+ By,
958
+ Conditions,
959
+ Inheriting,
960
+ Inherit,
961
+ Super,
962
+ Void,
963
+ Combination,
964
+ Mode,
965
+ Elements,
966
+ Join,
967
+ Inner,
968
+ Left,
969
+ Right,
970
+ Cross,
971
+ Full,
972
+ Outer,
375
973
  And,
376
974
  Or,
975
+ In
976
+ ];
977
+ const allTokens = [
978
+ WhiteSpace,
979
+ LineComment,
980
+ BlockComment,
981
+ SqlLineComment,
982
+ StringLiteral,
983
+ NumberLiteral,
984
+ EnumLiteral,
985
+ ...keywordTokens,
377
986
  Identifier,
378
987
  EqEq,
379
988
  NotEq,
380
989
  LtEq,
381
990
  GtEq,
991
+ FatArrow,
382
992
  At,
383
993
  Colon,
994
+ Dollar,
384
995
  Semicolon,
385
996
  Dot,
386
997
  Comma,
@@ -393,6 +1004,7 @@ const allTokens = [
393
1004
  Eq,
394
1005
  Lt,
395
1006
  Gt,
1007
+ Quest,
396
1008
  Star,
397
1009
  Plus,
398
1010
  Minus,
@@ -406,22 +1018,136 @@ const CdsLexer = new Lexer(allTokens);
406
1018
  * Chevrotain CstParser for ABAP CDS.
407
1019
  *
408
1020
  * Grammar coverage (see src/lib/grammar/*.ts for the per-topic index):
409
- * - DDL : table, structure, simple type, view entity, projection view,
410
- * abstract/custom entity
411
- * - DCL : role definitions (grant select on ... where ...)
412
- * - DDLX : metadata extensions (`annotate entity ... with { ... }`)
1021
+ * - DDL : table, table entity, structure, simple type, view entity
1022
+ * (select / projection, `root` / `writable`, provider contract),
1023
+ * abstract/custom/external entity, service, hierarchy, aspect,
1024
+ * table function, scalar function, annotation (DDLA), accesspolicy
1025
+ * - DCL : role definitions (grant select on ... where ..., inherit rules)
1026
+ * - DDLX : metadata extensions (`annotate entity|view ... with { ... }`)
1027
+ * - EXT : `extend view [entity]`, `extend abstract|custom entity`
413
1028
  * - SRVD : service definitions (`define service ... { expose ...; }`)
414
- * - Elements / associations / parameters / annotations
1029
+ * - Elements / associations / parameters / annotations / conditions
1030
+ *
1031
+ * Conditions (`on` / `where` / `having` / filters) are kept as opaque
1032
+ * `Expression { source, tokens }` nodes — the parser validates the grammar
1033
+ * shape but does not build an expression tree.
415
1034
  *
416
1035
  * Rules are kept in this single class (Chevrotain requirement). Logical
417
1036
  * groupings are delimited with section banners and documented in
418
1037
  * `src/lib/grammar/`.
419
1038
  */
420
- var CdsParser = class extends CstParser {
1039
+ var CdsParser = class CdsParser extends CstParser {
1040
+ /**
1041
+ * All token types that may be used as a CDS name.
1042
+ *
1043
+ * Every keyword may legally appear as an identifier in CDS (`A.on`,
1044
+ * `type : abap.char(1)`, `as select`, …), so the name set is derived from
1045
+ * the keyword list in `tokens.ts`. The list is the single source of truth
1046
+ * for the `cdsName` rule and for the bare-alias rule — the latter removes
1047
+ * the FROM-region starters.
1048
+ */
1049
+ static NAME_TOKENS = [Identifier, ...keywordTokens];
1050
+ static NAME_TOKEN_SET = new Set(CdsParser.NAME_TOKENS);
1051
+ /** Comparison operators that end a parenthesized *operand*. */
1052
+ static COMPARISON_TOKEN_SET = /* @__PURE__ */ new Set([
1053
+ Eq,
1054
+ EqEq,
1055
+ NotEq,
1056
+ Lt,
1057
+ LtEq,
1058
+ Gt,
1059
+ GtEq,
1060
+ Like,
1061
+ Between,
1062
+ In
1063
+ ]);
1064
+ /** Paren depth delta for `matchingParen`. */
1065
+ static PAREN_DELTA = /* @__PURE__ */ new Map([[LParen, 1], [RParen, -1]]);
1066
+ /** Bracket depth delta per token for the case-expression scan. */
1067
+ static DEPTH_DELTA = /* @__PURE__ */ new Map([
1068
+ [LParen, 1],
1069
+ [LBracket, 1],
1070
+ [RParen, -1],
1071
+ [RBracket, -1]
1072
+ ]);
1073
+ /** Tokens that mark a parenthesized group as a *condition*. */
1074
+ static CONDITION_MARKER_SET = /* @__PURE__ */ new Set([
1075
+ Eq,
1076
+ EqEq,
1077
+ NotEq,
1078
+ Lt,
1079
+ LtEq,
1080
+ Gt,
1081
+ GtEq,
1082
+ Like,
1083
+ Between,
1084
+ In,
1085
+ And,
1086
+ Or,
1087
+ Not
1088
+ ]);
1089
+ /**
1090
+ * Tokens that must not be read as a *bare* (implicit) source alias — they
1091
+ * start whatever may follow a data source in the FROM region (joins,
1092
+ * associations, aspect bindings, the `where`/`group by`/`having` clauses
1093
+ * and the set operators). An explicit `as <alias>` stays unrestricted.
1094
+ *
1095
+ * `one`/`many`/`exact`/`to` are excluded because they may start a join
1096
+ * cardinality (`many to one join`, `to one join`).
1097
+ */
1098
+ static FROM_REGION_STARTERS = /* @__PURE__ */ new Set([
1099
+ Association,
1100
+ Composition,
1101
+ Join,
1102
+ Inner,
1103
+ Left,
1104
+ Right,
1105
+ Cross,
1106
+ Full,
1107
+ One,
1108
+ Many,
1109
+ Exact,
1110
+ To,
1111
+ Union,
1112
+ Intersect,
1113
+ Except,
1114
+ Group,
1115
+ Having,
1116
+ Where,
1117
+ On,
1118
+ As,
1119
+ And,
1120
+ Or,
1121
+ Not,
1122
+ By,
1123
+ Bind,
1124
+ Redefine
1125
+ ]);
1126
+ /**
1127
+ * Tokens allowed inside a DCL `where` condition — everything except the
1128
+ * terminating semicolon. DCL conditions (literal / pfcg / aspect / user /
1129
+ * inherit / `if-then-else` / `void`) are captured opaquely.
1130
+ */
1131
+ static DCL_TOKENS = allTokens.filter((t) => ![
1132
+ "WhiteSpace",
1133
+ "LineComment",
1134
+ "BlockComment",
1135
+ "SqlLineComment",
1136
+ "Semicolon"
1137
+ ].includes(t.name));
1138
+ /** Tokens allowed inside a `{ … }` free-form block (DDLA / accesspolicy). */
1139
+ static BLOCK_TOKENS = allTokens.filter((t) => ![
1140
+ "WhiteSpace",
1141
+ "LineComment",
1142
+ "BlockComment",
1143
+ "SqlLineComment",
1144
+ "LBrace",
1145
+ "RBrace"
1146
+ ].includes(t.name));
421
1147
  constructor() {
422
1148
  super(allTokens, {
423
1149
  recoveryEnabled: true,
424
- maxLookahead: 4
1150
+ maxLookahead: 2
425
1151
  });
426
1152
  this.performSelfAnalysis();
427
1153
  }
@@ -432,11 +1158,16 @@ var CdsParser = class extends CstParser {
432
1158
  this.SUBRULE(this.definition);
433
1159
  });
434
1160
  definition = this.RULE("definition", () => {
435
- this.OR([{ ALT: () => this.SUBRULE(this.defineStatement) }, { ALT: () => this.SUBRULE(this.annotateStatement) }]);
1161
+ this.OR([
1162
+ { ALT: () => this.SUBRULE(this.defineStatement) },
1163
+ { ALT: () => this.SUBRULE(this.annotateStatement) },
1164
+ { ALT: () => this.SUBRULE(this.extendStatement) }
1165
+ ]);
436
1166
  });
437
1167
  defineStatement = this.RULE("defineStatement", () => {
438
1168
  this.CONSUME(Define);
439
1169
  this.OR([
1170
+ { ALT: () => this.SUBRULE(this.prefixedDefinition) },
440
1171
  { ALT: () => this.SUBRULE(this.tableDefinition) },
441
1172
  { ALT: () => this.SUBRULE(this.structureDefinition) },
442
1173
  { ALT: () => this.SUBRULE(this.viewEntityDefinition) },
@@ -444,11 +1175,34 @@ var CdsParser = class extends CstParser {
444
1175
  { ALT: () => this.SUBRULE(this.customEntityDefinition) },
445
1176
  { ALT: () => this.SUBRULE(this.simpleTypeDefinition) },
446
1177
  { ALT: () => this.SUBRULE(this.serviceDefinition) },
447
- { ALT: () => this.SUBRULE(this.roleDefinition) }
1178
+ { ALT: () => this.SUBRULE(this.roleDefinition) },
1179
+ { ALT: () => this.SUBRULE(this.hierarchyDefinition) },
1180
+ { ALT: () => this.SUBRULE(this.aspectDefinition) },
1181
+ { ALT: () => this.SUBRULE(this.externalEntityDefinition) },
1182
+ { ALT: () => this.SUBRULE(this.scalarFunctionDefinition) },
1183
+ { ALT: () => this.SUBRULE(this.annotationDefinition) },
1184
+ { ALT: () => this.SUBRULE(this.accessPolicyDefinition) }
448
1185
  ]);
449
1186
  });
450
- tableDefinition = this.RULE("tableDefinition", () => {
1187
+ /**
1188
+ * `define (writable|root)+ …` — view entities, external entities and root
1189
+ * table entities carry leading modifiers between `define` and the entity
1190
+ * keyword.
1191
+ */
1192
+ prefixedDefinition = this.RULE("prefixedDefinition", () => {
1193
+ this.AT_LEAST_ONE(() => {
1194
+ this.OR([{ ALT: () => this.CONSUME(Writable) }, { ALT: () => this.CONSUME(Root) }]);
1195
+ });
1196
+ this.OR2([
1197
+ { ALT: () => this.SUBRULE(this.viewEntityDefinition) },
1198
+ { ALT: () => this.SUBRULE(this.externalEntityDefinition) },
1199
+ { ALT: () => this.SUBRULE(this.rootTableEntityDefinition) }
1200
+ ]);
1201
+ });
1202
+ /** `table entity <name> { … }` — reachable via `define [root] table entity`. */
1203
+ rootTableEntityDefinition = this.RULE("rootTableEntityDefinition", () => {
451
1204
  this.CONSUME(Table);
1205
+ this.CONSUME(Entity);
452
1206
  this.SUBRULE(this.cdsName);
453
1207
  this.CONSUME(LBrace);
454
1208
  this.MANY(() => {
@@ -456,6 +1210,55 @@ var CdsParser = class extends CstParser {
456
1210
  });
457
1211
  this.CONSUME(RBrace);
458
1212
  });
1213
+ tableDefinition = this.RULE("tableDefinition", () => {
1214
+ this.CONSUME(Table);
1215
+ this.OR([
1216
+ {
1217
+ GATE: () => this.LA(1).tokenType === FunctionKeyword && this.isNameToken(this.LA(2)),
1218
+ ALT: () => this.SUBRULE(this.tableFunctionTail)
1219
+ },
1220
+ {
1221
+ GATE: () => this.LA(1).tokenType === Entity && this.isNameToken(this.LA(2)),
1222
+ ALT: () => {
1223
+ this.CONSUME(Entity);
1224
+ this.SUBRULE(this.cdsName);
1225
+ this.CONSUME(LBrace);
1226
+ this.MANY(() => {
1227
+ this.SUBRULE(this.tableMember);
1228
+ });
1229
+ this.CONSUME(RBrace);
1230
+ }
1231
+ },
1232
+ { ALT: () => {
1233
+ this.SUBRULE2(this.cdsName);
1234
+ this.CONSUME2(LBrace);
1235
+ this.MANY2(() => {
1236
+ this.SUBRULE2(this.tableMember);
1237
+ });
1238
+ this.CONSUME2(RBrace);
1239
+ } }
1240
+ ]);
1241
+ });
1242
+ tableFunctionTail = this.RULE("tableFunctionTail", () => {
1243
+ this.CONSUME(FunctionKeyword);
1244
+ this.SUBRULE(this.cdsName);
1245
+ this.OPTION(() => {
1246
+ this.SUBRULE(this.parametersClause);
1247
+ });
1248
+ this.CONSUME(Returns);
1249
+ this.CONSUME(LBrace);
1250
+ this.MANY(() => {
1251
+ this.SUBRULE(this.fieldDefinition);
1252
+ });
1253
+ this.CONSUME(RBrace);
1254
+ this.CONSUME(Implemented);
1255
+ this.CONSUME(By);
1256
+ this.CONSUME(Method);
1257
+ this.SUBRULE(this.qualifiedName);
1258
+ this.CONSUME(FatArrow);
1259
+ this.SUBRULE2(this.cdsName);
1260
+ this.OPTION2(() => this.CONSUME(Semicolon));
1261
+ });
459
1262
  structureDefinition = this.RULE("structureDefinition", () => {
460
1263
  this.CONSUME(Structure);
461
1264
  this.SUBRULE(this.cdsName);
@@ -467,21 +1270,40 @@ var CdsParser = class extends CstParser {
467
1270
  });
468
1271
  viewEntityDefinition = this.RULE("viewEntityDefinition", () => {
469
1272
  this.CONSUME(View);
470
- this.CONSUME(Entity);
1273
+ this.OPTION({
1274
+ GATE: () => this.isEntityModifier(),
1275
+ DEF: () => this.CONSUME(Entity)
1276
+ });
471
1277
  this.SUBRULE(this.cdsName);
472
- this.OPTION(() => {
1278
+ this.OPTION2(() => {
473
1279
  this.SUBRULE(this.parametersClause);
474
1280
  });
475
- this.CONSUME(As);
476
- this.OR([{ ALT: () => this.SUBRULE(this.selectFromClause) }, { ALT: () => this.SUBRULE(this.projectionOnClause) }]);
477
- this.OPTION2(() => {
478
- this.SUBRULE(this.elementList);
479
- });
480
1281
  this.OPTION3(() => {
481
- this.CONSUME(Where);
482
- this.SUBRULE(this.expression);
1282
+ this.CONSUME(Provider);
1283
+ this.CONSUME(Contract);
1284
+ this.SUBRULE2(this.cdsName);
483
1285
  });
1286
+ this.CONSUME(As);
1287
+ this.OR([{ ALT: () => this.SUBRULE(this.selectStatement) }, { ALT: () => this.SUBRULE(this.projectionTail) }]);
484
1288
  });
1289
+ /**
1290
+ * `true` when the `entity` after `view` is the view-entity modifier rather
1291
+ * than the view name — it is a modifier iff the following token is a name
1292
+ * other than `as`/`with`/`provider` (the tokens that may directly follow a
1293
+ * view name).
1294
+ */
1295
+ isEntityModifier() {
1296
+ if (this.LA(1).tokenType !== Entity) return false;
1297
+ const next = this.LA(2);
1298
+ if (!this.isNameToken(next)) return false;
1299
+ return ![
1300
+ As,
1301
+ With,
1302
+ Provider,
1303
+ Select,
1304
+ Projection
1305
+ ].includes(next.tokenType);
1306
+ }
485
1307
  abstractEntityDefinition = this.RULE("abstractEntityDefinition", () => {
486
1308
  this.CONSUME(Abstract);
487
1309
  this.CONSUME(Entity);
@@ -489,7 +1311,12 @@ var CdsParser = class extends CstParser {
489
1311
  this.OPTION(() => {
490
1312
  this.SUBRULE(this.parametersClause);
491
1313
  });
492
- this.SUBRULE(this.elementList);
1314
+ this.memberContext = "entity";
1315
+ try {
1316
+ this.SUBRULE(this.elementList);
1317
+ } finally {
1318
+ this.memberContext = "view";
1319
+ }
493
1320
  });
494
1321
  customEntityDefinition = this.RULE("customEntityDefinition", () => {
495
1322
  this.CONSUME(Custom);
@@ -498,25 +1325,276 @@ var CdsParser = class extends CstParser {
498
1325
  this.OPTION(() => {
499
1326
  this.SUBRULE(this.parametersClause);
500
1327
  });
501
- this.SUBRULE(this.elementList);
1328
+ this.memberContext = "entity";
1329
+ try {
1330
+ this.SUBRULE(this.elementList);
1331
+ } finally {
1332
+ this.memberContext = "view";
1333
+ }
502
1334
  });
503
- selectFromClause = this.RULE("selectFromClause", () => {
1335
+ /**
1336
+ * `external entity <name> [external name <obj>] [with parameters …] { … }
1337
+ * with federated data provided at runtime [;]`
1338
+ */
1339
+ externalEntityDefinition = this.RULE("externalEntityDefinition", () => {
1340
+ this.CONSUME(External);
1341
+ this.CONSUME(Entity);
1342
+ this.SUBRULE(this.cdsName);
1343
+ this.OPTION({
1344
+ GATE: () => this.LA(1).tokenType === External && this.LA(2).tokenType === Name,
1345
+ DEF: () => {
1346
+ this.CONSUME2(External);
1347
+ this.CONSUME(Name);
1348
+ this.SUBRULE2(this.cdsName);
1349
+ }
1350
+ });
1351
+ this.OPTION2(() => {
1352
+ this.SUBRULE(this.parametersClause);
1353
+ });
1354
+ this.CONSUME(LBrace);
1355
+ this.MANY(() => {
1356
+ this.SUBRULE(this.fieldDefinition);
1357
+ });
1358
+ this.CONSUME(RBrace);
1359
+ this.CONSUME(With);
1360
+ this.CONSUME(Federated);
1361
+ this.CONSUME(Data);
1362
+ this.CONSUME(Provided);
1363
+ this.CONSUME(AtKeyword);
1364
+ this.CONSUME(Runtime);
1365
+ this.OPTION3(() => this.CONSUME(Semicolon));
1366
+ });
1367
+ selectStatement = this.RULE("selectStatement", () => {
504
1368
  this.CONSUME(Select);
1369
+ this.OPTION(() => this.CONSUME(Distinct));
1370
+ this.CONSUME(From);
1371
+ this.SUBRULE(this.dataSource);
1372
+ this.MANY(() => {
1373
+ this.OR([
1374
+ { ALT: () => this.SUBRULE(this.joinClause) },
1375
+ { ALT: () => this.SUBRULE(this.associationTail) },
1376
+ { ALT: () => this.SUBRULE(this.bindAspect) }
1377
+ ]);
1378
+ });
1379
+ this.SUBRULE(this.elementList);
1380
+ this.SUBRULE(this.selectClauses);
1381
+ });
1382
+ selectClauses = this.RULE("selectClauses", () => {
505
1383
  this.OPTION(() => {
506
- this.CONSUME(From);
1384
+ this.CONSUME(Where);
1385
+ this.SUBRULE(this.condition);
507
1386
  });
508
- this.SUBRULE(this.dataSource);
1387
+ this.OPTION2(() => {
1388
+ this.CONSUME(Group);
1389
+ this.CONSUME(By);
1390
+ this.SUBRULE(this.qualifiedName);
1391
+ this.MANY(() => {
1392
+ this.CONSUME(Comma);
1393
+ this.SUBRULE2(this.qualifiedName);
1394
+ });
1395
+ });
1396
+ this.OPTION3(() => {
1397
+ this.CONSUME(Having);
1398
+ this.SUBRULE2(this.condition);
1399
+ });
1400
+ this.MANY2(() => {
1401
+ this.SUBRULE(this.setOperation);
1402
+ });
1403
+ });
1404
+ /** `union [all]|intersect|except select …` — nested set operation. */
1405
+ setOperation = this.RULE("setOperation", () => {
1406
+ this.OR([
1407
+ { ALT: () => {
1408
+ this.CONSUME(Union);
1409
+ this.OPTION(() => this.CONSUME(All));
1410
+ } },
1411
+ { ALT: () => this.CONSUME(Intersect) },
1412
+ { ALT: () => this.CONSUME(Except) }
1413
+ ]);
1414
+ this.SUBRULE(this.selectStatement);
509
1415
  });
510
- projectionOnClause = this.RULE("projectionOnClause", () => {
1416
+ /**
1417
+ * `as projection on <source> [bind aspect] [associations]
1418
+ * [redefined associations] { projection list } [where …]`
1419
+ */
1420
+ projectionTail = this.RULE("projectionTail", () => {
511
1421
  this.CONSUME(Projection);
512
1422
  this.CONSUME(On);
513
1423
  this.SUBRULE(this.dataSource);
1424
+ this.MANY(() => {
1425
+ this.OR([
1426
+ { ALT: () => this.SUBRULE(this.associationTail) },
1427
+ { ALT: () => this.SUBRULE(this.redefinedAssociation) },
1428
+ { ALT: () => this.SUBRULE(this.bindAspect) }
1429
+ ]);
1430
+ });
1431
+ this.SUBRULE(this.elementList);
1432
+ this.OPTION(() => {
1433
+ this.CONSUME(Where);
1434
+ this.SUBRULE(this.condition);
1435
+ });
514
1436
  });
1437
+ /**
1438
+ * A data source: `<name> [ ( <parameter bindings> ) ] [ as <alias> ]`.
1439
+ *
1440
+ * The parameter list precedes the alias, e.g.
1441
+ * `ZP_MM_PR_DOC_TF( mandt: $session.client ) as _PRDocList`.
1442
+ */
515
1443
  dataSource = this.RULE("dataSource", () => {
1444
+ this.SUBRULE(this.dataSourcePath);
1445
+ this.OPTION(() => {
1446
+ this.SUBRULE(this.dataSourceParameters);
1447
+ });
1448
+ this.OPTION2(() => {
1449
+ this.OR([{ ALT: () => {
1450
+ this.CONSUME(As);
1451
+ this.SUBRULE(this.cdsName);
1452
+ } }, { ALT: () => this.SUBRULE(this.bareAliasName) }]);
1453
+ });
1454
+ });
1455
+ /**
1456
+ * `<name>[<attr>].<name>…` — a FROM data-source path. Path segments may
1457
+ * carry `[ … ]` association filters/attribute bindings (captured opaquely),
1458
+ * e.g. `_order._items[1:currency = 'EUR']`.
1459
+ */
1460
+ dataSourcePath = this.RULE("dataSourcePath", () => {
1461
+ this.SUBRULE(this.pathSegment);
1462
+ this.MANY(() => {
1463
+ this.CONSUME(Dot);
1464
+ this.SUBRULE2(this.pathSegment);
1465
+ });
1466
+ });
1467
+ /**
1468
+ * `( <param> : <value> , … )` — parameter bindings of a data source.
1469
+ * At least one binding is required; `source()` is rejected.
1470
+ */
1471
+ dataSourceParameters = this.RULE("dataSourceParameters", () => {
1472
+ this.CONSUME(LParen);
1473
+ this.SUBRULE(this.dataSourceParameter);
1474
+ this.MANY(() => {
1475
+ this.CONSUME(Comma);
1476
+ this.SUBRULE2(this.dataSourceParameter);
1477
+ });
1478
+ this.CONSUME(RParen);
1479
+ });
1480
+ /** `<name> : <value>` — a single parameter binding. */
1481
+ dataSourceParameter = this.RULE("dataSourceParameter", () => {
516
1482
  this.SUBRULE(this.qualifiedName);
1483
+ this.CONSUME(Colon);
1484
+ this.SUBRULE(this.parameterBinding);
1485
+ });
1486
+ /**
1487
+ * The value bound to a source parameter. Kept as written in the source:
1488
+ * - `:P_Param` — parameter of the consuming view
1489
+ * - `$session.client`, `$parameters.P_X` — system variable
1490
+ * - `'X'`, `42`, `abap.dats'20200101'` — literal
1491
+ */
1492
+ parameterBinding = this.RULE("parameterBinding", () => {
1493
+ this.OR([
1494
+ { ALT: () => {
1495
+ this.CONSUME(Colon);
1496
+ this.SUBRULE(this.qualifiedName);
1497
+ } },
1498
+ { ALT: () => this.SUBRULE(this.systemVariable) },
1499
+ { ALT: () => this.SUBRULE(this.literal) }
1500
+ ]);
1501
+ });
1502
+ joinClause = this.RULE("joinClause", () => {
1503
+ this.OR([{ ALT: () => {
1504
+ this.CONSUME(Cross);
1505
+ this.CONSUME(Join);
1506
+ this.SUBRULE(this.joinOperand);
1507
+ } }, { ALT: () => {
1508
+ this.SUBRULE(this.joinPrefix);
1509
+ this.CONSUME2(Join);
1510
+ this.SUBRULE2(this.joinOperand);
1511
+ this.CONSUME(On);
1512
+ this.SUBRULE(this.condition);
1513
+ } }]);
1514
+ });
1515
+ /**
1516
+ * Optional join-type words and the optional word-form cardinality, both
1517
+ * preceding the `JOIN` keyword:
1518
+ * `left [outer]`, `right [outer]`, `inner`, `full [outer]`, `<empty>`
1519
+ * then `[exact one|one|many] to [exact] one|many` | `to [exact] one|many`.
1520
+ */
1521
+ joinPrefix = this.RULE("joinPrefix", () => {
517
1522
  this.OPTION(() => {
518
- this.OPTION2(() => this.CONSUME(As));
519
- this.SUBRULE(this.cdsName);
1523
+ this.OR([
1524
+ { ALT: () => {
1525
+ this.CONSUME(Left);
1526
+ this.OPTION2(() => this.CONSUME(Outer));
1527
+ } },
1528
+ { ALT: () => {
1529
+ this.CONSUME(Right);
1530
+ this.OPTION3(() => this.CONSUME2(Outer));
1531
+ } },
1532
+ { ALT: () => this.CONSUME(Inner) },
1533
+ { ALT: () => {
1534
+ this.CONSUME(Full);
1535
+ this.OPTION4(() => this.CONSUME3(Outer));
1536
+ } }
1537
+ ]);
1538
+ });
1539
+ this.OPTION5({
1540
+ GATE: () => this.isJoinCardinality(),
1541
+ DEF: () => this.SUBRULE(this.joinCardinality)
1542
+ });
1543
+ });
1544
+ /**
1545
+ * `[exact one|one|many] to (exact one|one|many)` or `to (exact one|one|many)`
1546
+ * — the word-form join cardinality, e.g. `many to one join`, `to one join`.
1547
+ */
1548
+ joinCardinality = this.RULE("joinCardinality", () => {
1549
+ this.OPTION(() => this.SUBRULE(this.cardinalWord));
1550
+ this.CONSUME(To);
1551
+ this.SUBRULE2(this.cardinalWord);
1552
+ });
1553
+ /** `exact one` | `one` | `many` — a cardinality bound written in words. */
1554
+ cardinalWord = this.RULE("cardinalWord", () => {
1555
+ this.OR([
1556
+ { ALT: () => {
1557
+ this.CONSUME(Exact);
1558
+ this.CONSUME(One);
1559
+ } },
1560
+ { ALT: () => this.CONSUME2(One) },
1561
+ { ALT: () => this.CONSUME(Many) }
1562
+ ]);
1563
+ });
1564
+ /**
1565
+ * Cheap lookahead for the word-form join cardinality: it ends with the
1566
+ * `JOIN` keyword. Matches `[src] to tgt` where
1567
+ * src ∈ {`exact one`,`one`,`many`} (optional) and
1568
+ * tgt ∈ {`exact one`,`one`,`many`}.
1569
+ */
1570
+ isJoinCardinality() {
1571
+ let i = 1;
1572
+ const typeAt = (n) => this.LA(n).tokenType;
1573
+ if (typeAt(i) === Exact && typeAt(i + 1) === One) i += 2;
1574
+ else if (typeAt(i) === One || typeAt(i) === Many) i += 1;
1575
+ if (typeAt(i) !== To) return false;
1576
+ i += 1;
1577
+ if (typeAt(i) === Exact && typeAt(i + 1) === One) i += 2;
1578
+ else if (typeAt(i) === One || typeAt(i) === Many) i += 1;
1579
+ else return false;
1580
+ return typeAt(i) === Join;
1581
+ }
1582
+ /**
1583
+ * A join source: a data source or a parenthesized join group
1584
+ * (`join (A join B on …) on …`).
1585
+ */
1586
+ joinOperand = this.RULE("joinOperand", () => {
1587
+ this.OR([{ ALT: () => {
1588
+ this.CONSUME(LParen);
1589
+ this.SUBRULE(this.joinGroup);
1590
+ this.CONSUME(RParen);
1591
+ } }, { ALT: () => this.SUBRULE(this.dataSource) }]);
1592
+ });
1593
+ /** `<source> [joins…]` — the content of a parenthesized join group. */
1594
+ joinGroup = this.RULE("joinGroup", () => {
1595
+ this.SUBRULE(this.joinOperand);
1596
+ this.MANY(() => {
1597
+ this.SUBRULE(this.joinClause);
520
1598
  });
521
1599
  });
522
1600
  elementList = this.RULE("elementList", () => {
@@ -528,16 +1606,28 @@ var CdsParser = class extends CstParser {
528
1606
  });
529
1607
  elementMember = this.RULE("elementMember", () => {
530
1608
  this.MANY(() => this.SUBRULE(this.annotation));
531
- this.OR([{ ALT: () => this.SUBRULE(this.associationTail) }, { ALT: () => this.SUBRULE(this.viewElementTail) }]);
1609
+ this.OR([{
1610
+ GATE: () => this.isAssociationDeclaration(),
1611
+ ALT: () => this.SUBRULE(this.associationTail)
1612
+ }, { ALT: () => this.SUBRULE(this.viewElementTail) }]);
532
1613
  });
533
1614
  viewElementTail = this.RULE("viewElementTail", () => {
534
- this.OPTION(() => this.CONSUME(Key));
535
- this.OPTION2(() => this.CONSUME(Virtual));
536
- this.OPTION3(() => this.CONSUME(Redirected));
1615
+ this.OPTION({
1616
+ GATE: () => this.isElementStart(this.LA(2)),
1617
+ DEF: () => this.CONSUME(Key)
1618
+ });
1619
+ this.OPTION2({
1620
+ GATE: () => this.isElementStart(this.LA(2)),
1621
+ DEF: () => this.CONSUME(Virtual)
1622
+ });
1623
+ this.OPTION3({
1624
+ GATE: () => this.isElementStart(this.LA(2)),
1625
+ DEF: () => this.CONSUME(Redirected)
1626
+ });
537
1627
  this.OR([{
538
1628
  GATE: () => this.isTypedField(),
539
1629
  ALT: () => this.SUBRULE(this.typedFieldTail)
540
- }, { ALT: () => this.SUBRULE(this.projectionExpression) }]);
1630
+ }, { ALT: () => this.SUBRULE(this.elementExpression) }]);
541
1631
  this.OPTION4(() => {
542
1632
  this.OR2([{ ALT: () => this.CONSUME(Semicolon) }, { ALT: () => this.CONSUME(Comma) }]);
543
1633
  });
@@ -552,51 +1642,275 @@ var CdsParser = class extends CstParser {
552
1642
  });
553
1643
  });
554
1644
  /**
555
- * projection expression:
556
- * <qualified-name> [as <alias>]
557
- * Kept intentionally narrow — full SQL expression support is out of scope.
1645
+ * Element expression — everything allowed in a select/projection list:
1646
+ * paths (`_assoc.field`, `_a[inner].f`), calls (`sum(x)`, `div(a,b,c)`),
1647
+ * window functions (`… over (partition by … order by …)`), casts, case
1648
+ * distinctions, arithmetic, literals, parameters (`:P_X`) and system
1649
+ * variables (`$session.client`, `$parameters.x`, `$projection.f`).
1650
+ *
1651
+ * Followed by an optional `as <alias>` and an optional element suffix
1652
+ * (`: localized`, `: redirected to [composition child|parent] <target>`).
1653
+ */
1654
+ elementExpression = this.RULE("elementExpression", () => {
1655
+ this.SUBRULE(this.operand);
1656
+ this.OPTION(() => {
1657
+ this.CONSUME(As);
1658
+ this.SUBRULE(this.cdsName);
1659
+ });
1660
+ this.OPTION2(() => {
1661
+ this.CONSUME(Colon);
1662
+ this.OR([{ ALT: () => this.CONSUME(Localized) }, { ALT: () => this.SUBRULE(this.redirection) }]);
1663
+ });
1664
+ });
1665
+ /**
1666
+ * `redirected to [composition child|parent] <target>` — association
1667
+ * redirection in projection views.
558
1668
  */
559
- projectionExpression = this.RULE("projectionExpression", () => {
1669
+ redirection = this.RULE("redirection", () => {
1670
+ this.CONSUME(Redirected);
1671
+ this.CONSUME(To);
1672
+ this.OPTION(() => {
1673
+ this.OR([{ ALT: () => {
1674
+ this.CONSUME(Composition);
1675
+ this.CONSUME(Child);
1676
+ } }, { ALT: () => this.CONSUME(Parent) }]);
1677
+ });
1678
+ this.SUBRULE(this.qualifiedName);
1679
+ });
1680
+ /**
1681
+ * `redefine association|composition <name> [[card:] <filter>] [as <alias>]
1682
+ * [redirected to …]` — redefined association of a projection view,
1683
+ * declared before the projection list.
1684
+ */
1685
+ redefinedAssociation = this.RULE("redefinedAssociation", () => {
1686
+ this.CONSUME(Redefine);
1687
+ this.OR([{ ALT: () => this.CONSUME(Association) }, { ALT: () => this.CONSUME(Composition) }]);
560
1688
  this.SUBRULE(this.qualifiedName);
561
1689
  this.OPTION(() => {
1690
+ this.SUBRULE(this.bracketTokens);
1691
+ });
1692
+ this.OPTION2(() => {
562
1693
  this.CONSUME(As);
563
1694
  this.SUBRULE(this.cdsName);
564
1695
  });
1696
+ this.OPTION3(() => {
1697
+ this.SUBRULE(this.redirection);
1698
+ });
565
1699
  });
566
- /** Cheap lookahead: `ident :` (optionally following `key`/`virtual`) → typed field. */
567
- isTypedField() {
1700
+ /**
1701
+ * `bind aspect <aspect> ( <element> => <target>, … ) [as <alias>]`
1702
+ * — aspect binding in the FROM region or projection view header.
1703
+ */
1704
+ bindAspect = this.RULE("bindAspect", () => {
1705
+ this.CONSUME(Bind);
1706
+ this.CONSUME(Aspect);
1707
+ this.SUBRULE(this.qualifiedName);
1708
+ this.CONSUME(LParen);
1709
+ this.SUBRULE(this.bindMapping);
1710
+ this.MANY(() => {
1711
+ this.CONSUME(Comma);
1712
+ this.SUBRULE2(this.bindMapping);
1713
+ });
1714
+ this.CONSUME(RParen);
1715
+ this.OPTION(() => {
1716
+ this.CONSUME(As);
1717
+ this.SUBRULE(this.cdsName);
1718
+ });
1719
+ });
1720
+ /** `<aspect element> => <target>` — one aspect binding mapping. */
1721
+ bindMapping = this.RULE("bindMapping", () => {
1722
+ this.SUBRULE(this.cdsName);
1723
+ this.CONSUME(FatArrow);
1724
+ this.SUBRULE(this.operand);
1725
+ });
1726
+ /** Cheap lookahead: `cast (` — the cast form of a projection element. */
1727
+ isCastExpression() {
568
1728
  const first = this.LA(1);
569
1729
  const second = this.LA(2);
570
1730
  if (!first || !second) return false;
571
- return (first.tokenType?.name === "Identifier" || false) && second.tokenType?.name === "Colon";
1731
+ return first.tokenType?.name === "Identifier" && first.image.toLowerCase() === "cast" && second.tokenType?.name === "LParen";
1732
+ }
1733
+ /**
1734
+ * `view` — the element list allows projection suffixes (`: localized`,
1735
+ * `: redirected to`); `entity` — abstract/custom entity bodies where a
1736
+ * token after `:` is always a type name.
1737
+ */
1738
+ memberContext = "view";
1739
+ /**
1740
+ * Cheap lookahead: `name :` → typed field. Any keyword may be a name
1741
+ * (`Type : abap.char(1)`), but `name : localized` / `name : redirected …`
1742
+ * are projection-element suffixes, not typed fields.
1743
+ */
1744
+ isTypedField() {
1745
+ const first = this.LA(1);
1746
+ const second = this.LA(2);
1747
+ const third = this.LA(3);
1748
+ if (!this.isNameToken(first) || second.tokenType !== Colon) return false;
1749
+ if (this.memberContext === "entity") return true;
1750
+ return ![Localized, Redirected].includes(third.tokenType);
1751
+ }
1752
+ isNameToken(token) {
1753
+ return CdsParser.NAME_TOKEN_SET.has(token.tokenType);
1754
+ }
1755
+ /**
1756
+ * `association|composition` in an element list is a declaration only when
1757
+ * association syntax follows: `[`, `to`, `of`, or a name (the target).
1758
+ * `association :`, `association,`, `association ;` … are elements named
1759
+ * `association`.
1760
+ */
1761
+ isAssociationDeclaration() {
1762
+ const first = this.LA(1).tokenType;
1763
+ if (first !== Association && first !== Composition) return false;
1764
+ const second = this.LA(2);
1765
+ if ([
1766
+ To,
1767
+ Of,
1768
+ LBracket
1769
+ ].includes(second.tokenType)) return true;
1770
+ if (second.tokenType === As) return false;
1771
+ return this.isNameToken(second);
1772
+ }
1773
+ /**
1774
+ * `true` when the token may begin an element expression — used to decide
1775
+ * whether `key`/`virtual`/`redirected` are element flags or the element
1776
+ * name itself.
1777
+ */
1778
+ isElementStart(token) {
1779
+ if (this.isNameToken(token)) return true;
1780
+ return [
1781
+ StringLiteral,
1782
+ NumberLiteral,
1783
+ EnumLiteral,
1784
+ Minus,
1785
+ Plus,
1786
+ LParen,
1787
+ Dollar
1788
+ ].includes(token.tokenType);
1789
+ }
1790
+ /** `true` when `LA(1)` begins a literal (incl. typed `abap.x'…'`). */
1791
+ isLiteralStart() {
1792
+ const t = this.LA(1).tokenType;
1793
+ if ([
1794
+ StringLiteral,
1795
+ NumberLiteral,
1796
+ EnumLiteral,
1797
+ True,
1798
+ False,
1799
+ Null,
1800
+ Minus
1801
+ ].includes(t)) return true;
1802
+ return this.isTypedLiteral();
1803
+ }
1804
+ /**
1805
+ * `( … )` in a condition operand — `true` when the parenthesized group is
1806
+ * itself a condition (contains a predicate-ish token at depth ≥ 1 and is
1807
+ * not immediately followed by a comparison operator, which would make the
1808
+ * group an operand of a comparison like `(a + b) = 1`).
1809
+ */
1810
+ isParenCondition() {
1811
+ if (this.LA(1).tokenType !== LParen) return false;
1812
+ const close = this.matchingParen(1);
1813
+ if (close < 0) return false;
1814
+ if (this.isComparisonToken(this.LA(close + 1))) return false;
1815
+ for (let i = 2; i < close; i += 1) if (this.isConditionMarker(this.LA(i))) return true;
1816
+ return false;
1817
+ }
1818
+ /** Index of the `)` matching the `(` at `from`, or -1 (EOF/unbalanced). */
1819
+ matchingParen(from) {
1820
+ let depth = 0;
1821
+ for (let i = from; i < from + 400; i += 1) {
1822
+ const t = this.LA(i).tokenType;
1823
+ if (t === EOF) return -1;
1824
+ depth += CdsParser.PAREN_DELTA.get(t) ?? 0;
1825
+ if (t === RParen && depth === 0) return i;
1826
+ }
1827
+ return -1;
1828
+ }
1829
+ isConditionMarker(tok) {
1830
+ return CdsParser.CONDITION_MARKER_SET.has(tok.tokenType) || this.isIsKeyword(tok);
1831
+ }
1832
+ isComparisonToken(tok) {
1833
+ return CdsParser.COMPARISON_TOKEN_SET.has(tok.tokenType) || this.isIsKeyword(tok);
1834
+ }
1835
+ isIsKeyword(tok) {
1836
+ return tok.tokenType === Identifier && tok.image.toLowerCase() === "is";
1837
+ }
1838
+ /**
1839
+ * `case` is a legal element name — treat it as a case expression only
1840
+ * when a `when` follows before the element ends.
1841
+ */
1842
+ isCaseExpression() {
1843
+ if (this.LA(1).tokenType !== Case) return false;
1844
+ let depth = 0;
1845
+ for (let i = 2; i < 500; i += 1) {
1846
+ const t = this.LA(i).tokenType;
1847
+ if (this.caseScanEnds(t, depth)) return false;
1848
+ if (depth === 0 && t === When) return true;
1849
+ depth += CdsParser.DEPTH_DELTA.get(t) ?? 0;
1850
+ }
1851
+ return false;
1852
+ }
1853
+ caseScanEnds(t, depth) {
1854
+ if (t === EOF) return true;
1855
+ if (depth !== 0) return false;
1856
+ return [
1857
+ RBrace,
1858
+ Semicolon,
1859
+ Comma,
1860
+ RParen,
1861
+ RBracket
1862
+ ].includes(t);
572
1863
  }
573
1864
  associationTail = this.RULE("associationTail", () => {
574
1865
  this.OR([{ ALT: () => this.CONSUME(Association) }, { ALT: () => this.CONSUME(Composition) }]);
575
1866
  this.OPTION(() => this.SUBRULE(this.cardinality));
576
- this.OR2([{ ALT: () => {
577
- this.CONSUME(To);
578
- this.OPTION2(() => {
579
- this.CONSUME(Redirected);
580
- this.CONSUME2(To);
581
- });
582
- } }, { ALT: () => {
583
- this.CONSUME(Of);
584
- this.OPTION3(() => {
585
- this.OR3([{ ALT: () => this.CONSUME(Many) }, { ALT: () => this.CONSUME(One) }]);
586
- });
587
- this.OPTION4(() => this.CONSUME3(To));
588
- } }]);
589
- this.SUBRULE(this.qualifiedName);
590
- this.OPTION5(() => {
1867
+ this.OR2([
1868
+ { ALT: () => {
1869
+ this.CONSUME(Of);
1870
+ this.OPTION2(() => this.SUBRULE(this.cardinalWord));
1871
+ this.OPTION3(() => this.CONSUME(To));
1872
+ this.SUBRULE(this.qualifiedName);
1873
+ } },
1874
+ {
1875
+ GATE: () => this.LA(1).tokenType === To && this.LA(2).tokenType === Parent && this.isNameToken(this.LA(3)),
1876
+ ALT: () => {
1877
+ this.CONSUME2(To);
1878
+ this.CONSUME(Parent);
1879
+ this.SUBRULE2(this.qualifiedName);
1880
+ }
1881
+ },
1882
+ { ALT: () => {
1883
+ this.CONSUME3(To);
1884
+ this.OPTION4(() => this.SUBRULE3(this.cardinalWord));
1885
+ this.OPTION5(() => {
1886
+ this.CONSUME(Redirected);
1887
+ this.CONSUME4(To);
1888
+ });
1889
+ this.SUBRULE3(this.qualifiedName);
1890
+ } },
1891
+ {
1892
+ GATE: () => ![To, Of].includes(this.LA(1).tokenType),
1893
+ ALT: () => {
1894
+ this.SUBRULE4(this.qualifiedName);
1895
+ }
1896
+ }
1897
+ ]);
1898
+ this.OPTION6(() => {
591
1899
  this.CONSUME(As);
592
1900
  this.SUBRULE(this.cdsName);
593
1901
  });
594
- this.OPTION6(() => {
1902
+ this.OPTION7(() => {
595
1903
  this.CONSUME(On);
596
- this.SUBRULE(this.expression);
1904
+ this.SUBRULE(this.condition);
597
1905
  });
598
- this.OPTION7(() => {
599
- this.OR4([{ ALT: () => this.CONSUME(Semicolon) }, { ALT: () => this.CONSUME(Comma) }]);
1906
+ this.OPTION8(() => {
1907
+ this.CONSUME(With);
1908
+ this.CONSUME(Default);
1909
+ this.CONSUME(Filter);
1910
+ this.SUBRULE2(this.condition);
1911
+ });
1912
+ this.OPTION9(() => {
1913
+ this.OR3([{ ALT: () => this.CONSUME(Semicolon) }, { ALT: () => this.CONSUME(Comma) }]);
600
1914
  });
601
1915
  });
602
1916
  /** `[<lower>..<upper>]`, `[<n>]`, `[*]`, `[<lower>..*]` */
@@ -656,28 +1970,453 @@ var CdsParser = class extends CstParser {
656
1970
  });
657
1971
  this.CONSUME(Semicolon);
658
1972
  });
1973
+ scalarFunctionDefinition = this.RULE("scalarFunctionDefinition", () => {
1974
+ this.CONSUME(Scalar);
1975
+ this.CONSUME(FunctionKeyword);
1976
+ this.SUBRULE(this.cdsName);
1977
+ this.OPTION(() => {
1978
+ this.SUBRULE(this.parametersClause);
1979
+ });
1980
+ this.CONSUME(Returns);
1981
+ this.SUBRULE(this.typeReference);
1982
+ this.OPTION2(() => this.CONSUME(Semicolon));
1983
+ });
1984
+ hierarchyDefinition = this.RULE("hierarchyDefinition", () => {
1985
+ this.CONSUME(Hierarchy);
1986
+ this.SUBRULE(this.cdsName);
1987
+ this.OPTION(() => {
1988
+ this.SUBRULE(this.parametersClause);
1989
+ });
1990
+ this.CONSUME(As);
1991
+ this.CONSUME(Parent);
1992
+ this.CONSUME(Child);
1993
+ this.CONSUME2(Hierarchy);
1994
+ this.CONSUME(LParen);
1995
+ this.CONSUME(Source);
1996
+ this.SUBRULE(this.qualifiedName);
1997
+ this.CONSUME2(Child);
1998
+ this.CONSUME(To);
1999
+ this.CONSUME2(Parent);
2000
+ this.CONSUME(Association);
2001
+ this.SUBRULE2(this.cdsName);
2002
+ this.MANY(() => {
2003
+ this.OR([
2004
+ { ALT: () => this.SUBRULE(this.hierarchyPeriod) },
2005
+ { ALT: () => this.SUBRULE(this.hierarchyDirectory) },
2006
+ { ALT: () => this.SUBRULE(this.hierarchyStart) }
2007
+ ]);
2008
+ });
2009
+ this.CONSUME(Siblings);
2010
+ this.CONSUME(Order);
2011
+ this.CONSUME(By);
2012
+ this.SUBRULE(this.sortItem);
2013
+ this.MANY2(() => {
2014
+ this.CONSUME(Comma);
2015
+ this.SUBRULE2(this.sortItem);
2016
+ });
2017
+ this.MANY3(() => {
2018
+ this.OR2([
2019
+ { ALT: () => this.SUBRULE(this.hierarchyDepth) },
2020
+ { ALT: () => this.SUBRULE(this.hierarchyNodetype) },
2021
+ { ALT: () => this.SUBRULE(this.hierarchyLoad) },
2022
+ { ALT: () => this.SUBRULE(this.hierarchyMultiple) },
2023
+ { ALT: () => this.SUBRULE(this.hierarchyOrphans) },
2024
+ { ALT: () => this.SUBRULE(this.hierarchyCycles) },
2025
+ { ALT: () => this.SUBRULE(this.hierarchyGenerate) },
2026
+ { ALT: () => this.SUBRULE(this.hierarchyCache) }
2027
+ ]);
2028
+ });
2029
+ this.CONSUME(RParen);
2030
+ this.SUBRULE(this.elementList);
2031
+ });
2032
+ /** `period from <f> to <f> [valid from <f> to <f>]` */
2033
+ hierarchyPeriod = this.RULE("hierarchyPeriod", () => {
2034
+ this.CONSUME(Period);
2035
+ this.CONSUME(From);
2036
+ this.SUBRULE(this.qualifiedName);
2037
+ this.CONSUME(To);
2038
+ this.SUBRULE2(this.qualifiedName);
2039
+ this.OPTION(() => {
2040
+ this.CONSUME(Valid);
2041
+ this.CONSUME2(From);
2042
+ this.SUBRULE3(this.qualifiedName);
2043
+ this.CONSUME2(To);
2044
+ this.SUBRULE4(this.qualifiedName);
2045
+ });
2046
+ });
2047
+ /** `directory <entity> filter by <cond>` */
2048
+ hierarchyDirectory = this.RULE("hierarchyDirectory", () => {
2049
+ this.CONSUME(Directory);
2050
+ this.SUBRULE(this.qualifiedName);
2051
+ this.CONSUME(Filter);
2052
+ this.CONSUME(By);
2053
+ this.SUBRULE(this.condition);
2054
+ });
2055
+ /** `start where <cond>` */
2056
+ hierarchyStart = this.RULE("hierarchyStart", () => {
2057
+ this.CONSUME(Start);
2058
+ this.CONSUME(Where);
2059
+ this.SUBRULE(this.condition);
2060
+ });
2061
+ /** `depth <field>|<number>` */
2062
+ hierarchyDepth = this.RULE("hierarchyDepth", () => {
2063
+ this.CONSUME(Depth);
2064
+ this.OR([{ ALT: () => this.SUBRULE(this.qualifiedName) }, { ALT: () => this.CONSUME(NumberLiteral) }]);
2065
+ });
2066
+ /** `nodetype <field>` */
2067
+ hierarchyNodetype = this.RULE("hierarchyNodetype", () => {
2068
+ this.CONSUME(Nodetype);
2069
+ this.SUBRULE(this.qualifiedName);
2070
+ });
2071
+ /** `load bulk|incremental|<sysvar>|<field>` */
2072
+ hierarchyLoad = this.RULE("hierarchyLoad", () => {
2073
+ this.CONSUME(Load);
2074
+ this.OR([
2075
+ {
2076
+ GATE: () => this.LA(1).tokenType === Bulk,
2077
+ ALT: () => this.CONSUME(Bulk)
2078
+ },
2079
+ {
2080
+ GATE: () => this.LA(1).tokenType === Incremental,
2081
+ ALT: () => this.CONSUME(Incremental)
2082
+ },
2083
+ { ALT: () => this.SUBRULE(this.systemVariable) },
2084
+ { ALT: () => this.SUBRULE(this.qualifiedName) }
2085
+ ]);
2086
+ });
2087
+ /** `multiple parents not allowed|leaves only|allowed` */
2088
+ hierarchyMultiple = this.RULE("hierarchyMultiple", () => {
2089
+ this.CONSUME(Multiple);
2090
+ this.CONSUME(Parents);
2091
+ this.OR([
2092
+ { ALT: () => {
2093
+ this.CONSUME(Not);
2094
+ this.CONSUME(Allowed);
2095
+ } },
2096
+ { ALT: () => {
2097
+ this.CONSUME(Leaves);
2098
+ this.CONSUME(Only);
2099
+ } },
2100
+ { ALT: () => this.CONSUME2(Allowed) }
2101
+ ]);
2102
+ });
2103
+ /** `orphans ignore|error|root` */
2104
+ hierarchyOrphans = this.RULE("hierarchyOrphans", () => {
2105
+ this.CONSUME(Orphans);
2106
+ this.OR([
2107
+ { ALT: () => this.CONSUME(Ignore) },
2108
+ { ALT: () => this.CONSUME(ErrorKeyword) },
2109
+ { ALT: () => this.CONSUME(Root) }
2110
+ ]);
2111
+ });
2112
+ /** `cycles error|breakup` */
2113
+ hierarchyCycles = this.RULE("hierarchyCycles", () => {
2114
+ this.CONSUME(Cycles);
2115
+ this.OR([{ ALT: () => this.CONSUME(ErrorKeyword) }, { ALT: () => this.CONSUME(Breakup) }]);
2116
+ });
2117
+ /** `generate spantree` */
2118
+ hierarchyGenerate = this.RULE("hierarchyGenerate", () => {
2119
+ this.CONSUME(Generate);
2120
+ this.CONSUME(Spantree);
2121
+ });
2122
+ /** `cache on|off|force` */
2123
+ hierarchyCache = this.RULE("hierarchyCache", () => {
2124
+ this.CONSUME(Cache);
2125
+ this.OR([
2126
+ { ALT: () => this.CONSUME(On) },
2127
+ { ALT: () => this.CONSUME(Off) },
2128
+ { ALT: () => this.CONSUME(Force) }
2129
+ ]);
2130
+ });
2131
+ /** `<field> [ascending|descending]` — one ORDER BY item. */
2132
+ sortItem = this.RULE("sortItem", () => {
2133
+ this.SUBRULE(this.qualifiedName);
2134
+ this.OPTION(() => {
2135
+ this.OR([{ ALT: () => this.CONSUME(Ascending) }, { ALT: () => this.CONSUME(Descending) }]);
2136
+ });
2137
+ });
2138
+ aspectDefinition = this.RULE("aspectDefinition", () => {
2139
+ this.CONSUME(Aspect);
2140
+ this.SUBRULE(this.cdsName);
2141
+ this.OPTION(() => {
2142
+ this.CONSUME(LBrace);
2143
+ this.MANY(() => {
2144
+ this.SUBRULE(this.aspectElement);
2145
+ });
2146
+ this.CONSUME(RBrace);
2147
+ });
2148
+ });
2149
+ aspectElement = this.RULE("aspectElement", () => {
2150
+ this.MANY(() => this.SUBRULE(this.annotation));
2151
+ this.OPTION({
2152
+ GATE: () => this.isNameToken(this.LA(2)),
2153
+ DEF: () => this.CONSUME(Key)
2154
+ });
2155
+ this.SUBRULE(this.cdsName);
2156
+ this.CONSUME(Colon);
2157
+ this.OR([{
2158
+ GATE: () => this.isAspectTypedValue(),
2159
+ ALT: () => this.SUBRULE(this.typeReference)
2160
+ }, { ALT: () => this.SUBRULE(this.operand) }]);
2161
+ this.CONSUME(Semicolon);
2162
+ });
2163
+ /**
2164
+ * Lookahead for `name : <typing>;` inside an aspect element — true when the
2165
+ * token sequence after the colon is a type reference followed by `;`.
2166
+ */
2167
+ isAspectTypedValue() {
2168
+ const typeAt = (n) => this.LA(n).tokenType;
2169
+ if (typeAt(1) === Abap) return this.abapTypeEndsAtSemicolon();
2170
+ if (!this.isNameToken(this.LA(1))) return false;
2171
+ let i = 2;
2172
+ while (typeAt(i) === Dot && this.isNameToken(this.LA(i + 1))) i += 2;
2173
+ return typeAt(i) === Semicolon;
2174
+ }
2175
+ /** `abap.x[(n[,m])]` followed by `;` — a builtin aspect type. */
2176
+ abapTypeEndsAtSemicolon() {
2177
+ const typeAt = (n) => this.LA(n).tokenType;
2178
+ if (typeAt(2) !== Dot || !this.isNameToken(this.LA(3))) return false;
2179
+ let i = 4;
2180
+ if (typeAt(i) === LParen) {
2181
+ while (typeAt(i) !== RParen && typeAt(i) !== EOF) i += 1;
2182
+ i += 1;
2183
+ }
2184
+ return typeAt(i) === Semicolon;
2185
+ }
2186
+ annotationDefinition = this.RULE("annotationDefinition", () => {
2187
+ this.CONSUME(AnnotationKeyword);
2188
+ this.SUBRULE(this.cdsName);
2189
+ this.OR([{ ALT: () => {
2190
+ this.CONSUME(LBrace);
2191
+ this.SUBRULE(this.tokenBlock);
2192
+ this.CONSUME(RBrace);
2193
+ } }, { ALT: () => {
2194
+ this.CONSUME(Colon);
2195
+ this.SUBRULE(this.simpleTail);
2196
+ } }]);
2197
+ });
2198
+ /** `: <anything>;` — a colon-led definition tail terminated by `;`. */
2199
+ simpleTail = this.RULE("simpleTail", () => {
2200
+ this.MANY(() => {
2201
+ this.OR(CdsParser.DCL_TOKENS.map((tokenType) => ({ ALT: () => this.CONSUME(tokenType) })));
2202
+ });
2203
+ this.CONSUME(Semicolon);
2204
+ });
2205
+ accessPolicyDefinition = this.RULE("accessPolicyDefinition", () => {
2206
+ this.CONSUME(Accesspolicy);
2207
+ this.SUBRULE(this.cdsName);
2208
+ this.CONSUME(LBrace);
2209
+ this.SUBRULE(this.tokenBlock);
2210
+ this.CONSUME(RBrace);
2211
+ });
2212
+ /**
2213
+ * Balanced `{ … }` block content — any tokens, with nested brace groups.
2214
+ * Used for DDLA annotation bodies and access policy definitions.
2215
+ */
2216
+ tokenBlock = this.RULE("tokenBlock", () => {
2217
+ this.MANY(() => {
2218
+ this.OR([{ ALT: () => this.SUBRULE(this.tokenGroup) }, { ALT: () => {
2219
+ this.OR2(CdsParser.BLOCK_TOKENS.map((tokenType) => ({ ALT: () => this.CONSUME(tokenType) })));
2220
+ } }]);
2221
+ });
2222
+ });
2223
+ tokenGroup = this.RULE("tokenGroup", () => {
2224
+ this.CONSUME(LBrace);
2225
+ this.SUBRULE(this.tokenBlock);
2226
+ this.CONSUME(RBrace);
2227
+ });
659
2228
  roleDefinition = this.RULE("roleDefinition", () => {
660
2229
  this.CONSUME(Role);
661
2230
  this.SUBRULE(this.cdsName);
2231
+ this.OPTION(() => this.CONSUME(Switchable));
662
2232
  this.CONSUME(LBrace);
663
2233
  this.MANY(() => {
664
- this.SUBRULE(this.grantStatement);
2234
+ this.SUBRULE(this.roleMember);
665
2235
  });
666
2236
  this.CONSUME(RBrace);
667
2237
  });
2238
+ roleMember = this.RULE("roleMember", () => {
2239
+ this.OR([
2240
+ { ALT: () => this.SUBRULE(this.grantStatement) },
2241
+ { ALT: () => this.SUBRULE(this.inheritStatement) },
2242
+ { ALT: () => this.SUBRULE(this.inheritingStatement) }
2243
+ ]);
2244
+ });
2245
+ /**
2246
+ * `grant select on <entity> [with optional elements ( … )]
2247
+ * [combination mode or|and | redefinition] [inherit <role> [and|or <cond>]]
2248
+ * [where <dcl_cond>] ;`
2249
+ */
668
2250
  grantStatement = this.RULE("grantStatement", () => {
669
2251
  this.CONSUME(Grant);
670
2252
  this.CONSUME(Select);
671
2253
  this.CONSUME(On);
672
2254
  this.SUBRULE(this.qualifiedName);
2255
+ this.MANY(() => {
2256
+ this.SUBRULE(this.grantClause);
2257
+ });
2258
+ this.CONSUME(Semicolon);
2259
+ });
2260
+ /** One trailing clause of `grant select on <entity>`: with-optional-elements | combination-mode/redefinition | inherit | where. */
2261
+ grantClause = this.RULE("grantClause", () => {
2262
+ this.OR([
2263
+ { ALT: () => this.SUBRULE(this.grantOptionalElements) },
2264
+ { ALT: () => this.SUBRULE(this.grantCombination) },
2265
+ { ALT: () => this.SUBRULE(this.grantInherit) },
2266
+ { ALT: () => this.SUBRULE(this.grantWhere) }
2267
+ ]);
2268
+ });
2269
+ /** `with optional elements ( <name> [default true|false], … )` */
2270
+ grantOptionalElements = this.RULE("grantOptionalElements", () => {
2271
+ this.CONSUME(With);
2272
+ this.CONSUME(Optional);
2273
+ this.CONSUME(Elements);
2274
+ this.CONSUME(LParen);
2275
+ this.SUBRULE(this.optionalElement);
2276
+ this.MANY(() => {
2277
+ this.CONSUME(Comma);
2278
+ this.SUBRULE2(this.optionalElement);
2279
+ });
2280
+ this.CONSUME(RParen);
2281
+ });
2282
+ /** `combination mode or|and` | `redefinition` */
2283
+ grantCombination = this.RULE("grantCombination", () => {
2284
+ this.OR([{ ALT: () => {
2285
+ this.CONSUME(Combination);
2286
+ this.CONSUME(Mode);
2287
+ this.OR2([{ ALT: () => this.CONSUME(Or) }, { ALT: () => this.CONSUME(And) }]);
2288
+ } }, { ALT: () => this.CONSUME(Redefinition) }]);
2289
+ });
2290
+ /** `inherit <role> [and|or <dcl_cond>]` */
2291
+ grantInherit = this.RULE("grantInherit", () => {
2292
+ this.CONSUME(Inherit);
2293
+ this.SUBRULE(this.qualifiedName);
2294
+ this.OPTION(() => {
2295
+ this.OR([{ ALT: () => this.CONSUME(And) }, { ALT: () => this.CONSUME(Or) }]);
2296
+ this.SUBRULE(this.dclCondition);
2297
+ });
2298
+ });
2299
+ /** `where <dcl_cond>` */
2300
+ grantWhere = this.RULE("grantWhere", () => {
2301
+ this.CONSUME(Where);
2302
+ this.SUBRULE(this.dclCondition);
2303
+ });
2304
+ /** One entry of `with optional elements ( … )`: `name [default true|false]`. */
2305
+ optionalElement = this.RULE("optionalElement", () => {
2306
+ this.SUBRULE(this.qualifiedName);
2307
+ this.OPTION(() => {
2308
+ this.CONSUME(Default);
2309
+ this.OR([{ ALT: () => this.CONSUME(True) }, { ALT: () => this.CONSUME(False) }]);
2310
+ });
2311
+ });
2312
+ /**
2313
+ * `inherit <role> for grant select on <entity> [where …] ;`
2314
+ * — the standalone inherit rule of a role body.
2315
+ */
2316
+ inheritStatement = this.RULE("inheritStatement", () => {
2317
+ this.CONSUME(Inherit);
2318
+ this.SUBRULE(this.qualifiedName);
673
2319
  this.OPTION(() => {
2320
+ this.CONSUME(For);
2321
+ this.CONSUME(Grant);
2322
+ this.CONSUME(Select);
2323
+ this.CONSUME(On);
2324
+ this.SUBRULE2(this.qualifiedName);
2325
+ });
2326
+ this.OPTION2(() => {
674
2327
  this.CONSUME(Where);
675
- this.SUBRULE(this.expression);
2328
+ this.SUBRULE(this.dclCondition);
2329
+ });
2330
+ this.CONSUME(Semicolon);
2331
+ });
2332
+ /**
2333
+ * `inheriting conditions from entity <name>|super [default true|false] … ;`
2334
+ */
2335
+ inheritingStatement = this.RULE("inheritingStatement", () => {
2336
+ this.CONSUME(Inheriting);
2337
+ this.CONSUME(Conditions);
2338
+ this.CONSUME(From);
2339
+ this.OR([{ ALT: () => {
2340
+ this.CONSUME(Entity);
2341
+ this.SUBRULE(this.qualifiedName);
2342
+ } }, { ALT: () => this.CONSUME(Super) }]);
2343
+ this.OPTION(() => {
2344
+ this.CONSUME(Default);
2345
+ this.OR2([{ ALT: () => this.CONSUME(True) }, { ALT: () => this.CONSUME(False) }]);
2346
+ });
2347
+ this.OPTION2(() => {
2348
+ this.SUBRULE(this.dclCondition);
676
2349
  });
677
2350
  this.CONSUME(Semicolon);
678
2351
  });
2352
+ /**
2353
+ * A DCL access condition — captured opaquely until the terminating `;`.
2354
+ * Covers literal, pfcg, aspect, user, inherit and `if-then-else` conditions
2355
+ * plus `true`/`false`/`void` and `and`/`or`/`not` combinations.
2356
+ */
2357
+ dclCondition = this.RULE("dclCondition", () => {
2358
+ this.AT_LEAST_ONE(() => {
2359
+ this.OR(CdsParser.DCL_TOKENS.map((tokenType) => ({ ALT: () => this.CONSUME(tokenType) })));
2360
+ });
2361
+ });
2362
+ extendStatement = this.RULE("extendStatement", () => {
2363
+ this.CONSUME(Extend);
2364
+ this.OR([
2365
+ { ALT: () => {
2366
+ this.CONSUME(View);
2367
+ this.OPTION({
2368
+ GATE: () => this.isEntityModifier(),
2369
+ DEF: () => this.CONSUME(Entity)
2370
+ });
2371
+ this.SUBRULE(this.qualifiedName);
2372
+ } },
2373
+ { ALT: () => {
2374
+ this.CONSUME(Abstract);
2375
+ this.CONSUME2(Entity);
2376
+ this.SUBRULE2(this.qualifiedName);
2377
+ } },
2378
+ { ALT: () => {
2379
+ this.CONSUME(Custom);
2380
+ this.CONSUME3(Entity);
2381
+ this.SUBRULE3(this.qualifiedName);
2382
+ } }
2383
+ ]);
2384
+ this.CONSUME(With);
2385
+ this.MANY(() => {
2386
+ this.SUBRULE(this.associationTail);
2387
+ });
2388
+ this.SUBRULE(this.elementList);
2389
+ this.OPTION2(() => {
2390
+ this.CONSUME(Group);
2391
+ this.CONSUME(By);
2392
+ this.SUBRULE4(this.qualifiedName);
2393
+ this.MANY2(() => {
2394
+ this.CONSUME(Comma);
2395
+ this.SUBRULE5(this.qualifiedName);
2396
+ });
2397
+ });
2398
+ this.MANY3(() => {
2399
+ this.SUBRULE(this.extendSetOperation);
2400
+ });
2401
+ this.OPTION3(() => this.CONSUME(Semicolon));
2402
+ });
2403
+ /** `union [all]|intersect|except { … }` — extension-level set operation. */
2404
+ extendSetOperation = this.RULE("extendSetOperation", () => {
2405
+ this.OR([
2406
+ { ALT: () => {
2407
+ this.CONSUME(Union);
2408
+ this.OPTION(() => this.CONSUME(All));
2409
+ } },
2410
+ { ALT: () => this.CONSUME(Intersect) },
2411
+ { ALT: () => this.CONSUME(Except) }
2412
+ ]);
2413
+ this.SUBRULE(this.elementList);
2414
+ });
679
2415
  tableMember = this.RULE("tableMember", () => {
680
- this.OR([{ ALT: () => this.SUBRULE(this.includeDirective) }, { ALT: () => this.SUBRULE(this.fieldDefinition) }]);
2416
+ this.OR([{
2417
+ GATE: () => this.LA(1).tokenType === Include && this.isNameToken(this.LA(2)),
2418
+ ALT: () => this.SUBRULE(this.includeDirective)
2419
+ }, { ALT: () => this.SUBRULE(this.fieldDefinition) }]);
681
2420
  });
682
2421
  includeDirective = this.RULE("includeDirective", () => {
683
2422
  this.CONSUME(Include);
@@ -693,8 +2432,9 @@ var CdsParser = class extends CstParser {
693
2432
  this.MANY(() => {
694
2433
  this.SUBRULE(this.annotation);
695
2434
  });
696
- this.OPTION(() => {
697
- this.CONSUME(Key);
2435
+ this.OPTION({
2436
+ GATE: () => this.isNameToken(this.LA(2)),
2437
+ DEF: () => this.CONSUME(Key)
698
2438
  });
699
2439
  this.SUBRULE(this.cdsName);
700
2440
  this.CONSUME(Colon);
@@ -707,7 +2447,7 @@ var CdsParser = class extends CstParser {
707
2447
  });
708
2448
  annotateStatement = this.RULE("annotateStatement", () => {
709
2449
  this.CONSUME(Annotate);
710
- this.CONSUME(Entity);
2450
+ this.OR([{ ALT: () => this.CONSUME(Entity) }, { ALT: () => this.CONSUME(View) }]);
711
2451
  this.SUBRULE(this.cdsName);
712
2452
  this.CONSUME(With);
713
2453
  this.CONSUME(LBrace);
@@ -720,7 +2460,7 @@ var CdsParser = class extends CstParser {
720
2460
  this.MANY(() => {
721
2461
  this.SUBRULE(this.annotation);
722
2462
  });
723
- this.SUBRULE(this.cdsName);
2463
+ this.SUBRULE(this.qualifiedName);
724
2464
  this.CONSUME(Semicolon);
725
2465
  });
726
2466
  topLevelAnnotation = this.RULE("topLevelAnnotation", () => {
@@ -778,14 +2518,120 @@ var CdsParser = class extends CstParser {
778
2518
  this.SUBRULE(this.annotationValue);
779
2519
  });
780
2520
  literal = this.RULE("literal", () => {
2521
+ this.OPTION(() => this.CONSUME(Minus));
781
2522
  this.OR([
782
2523
  { ALT: () => this.CONSUME(StringLiteral) },
783
2524
  { ALT: () => this.CONSUME(NumberLiteral) },
784
2525
  { ALT: () => this.CONSUME(EnumLiteral) },
785
2526
  { ALT: () => this.CONSUME(True) },
786
- { ALT: () => this.CONSUME(False) }
2527
+ { ALT: () => this.CONSUME(False) },
2528
+ { ALT: () => this.CONSUME(Null) },
2529
+ {
2530
+ GATE: () => this.isTypedLiteral(),
2531
+ ALT: () => {
2532
+ this.CONSUME(Abap);
2533
+ this.CONSUME(Dot);
2534
+ this.SUBRULE(this.cdsName);
2535
+ this.CONSUME2(StringLiteral);
2536
+ }
2537
+ }
2538
+ ]);
2539
+ });
2540
+ /** Cheap lookahead: `abap.<type>'…'` — a typed literal. */
2541
+ isTypedLiteral() {
2542
+ return this.LA(1).tokenType === Abap && this.LA(2).tokenType === Dot && this.isNameToken(this.LA(3)) && this.LA(4).tokenType === StringLiteral;
2543
+ }
2544
+ condition = this.RULE("condition", () => {
2545
+ this.SUBRULE(this.conditionOperand);
2546
+ this.MANY(() => {
2547
+ this.OR([{ ALT: () => this.CONSUME(And) }, { ALT: () => this.CONSUME(Or) }]);
2548
+ this.SUBRULE2(this.conditionOperand);
2549
+ });
2550
+ });
2551
+ conditionOperand = this.RULE("conditionOperand", () => {
2552
+ this.OPTION(() => this.CONSUME(Not));
2553
+ this.OR([{
2554
+ GATE: () => this.isParenCondition(),
2555
+ ALT: () => {
2556
+ this.CONSUME(LParen);
2557
+ this.SUBRULE(this.condition);
2558
+ this.CONSUME(RParen);
2559
+ }
2560
+ }, { ALT: () => {
2561
+ this.SUBRULE(this.operand);
2562
+ this.SUBRULE(this.predicate);
2563
+ } }]);
2564
+ });
2565
+ /** The mandatory predicate part of a strict condition operand. */
2566
+ predicate = this.RULE("predicate", () => {
2567
+ this.OR([
2568
+ { ALT: () => {
2569
+ this.OR2([
2570
+ { ALT: () => this.CONSUME(EqEq) },
2571
+ { ALT: () => this.CONSUME(Eq) },
2572
+ { ALT: () => this.CONSUME(NotEq) },
2573
+ { ALT: () => this.CONSUME(LtEq) },
2574
+ { ALT: () => this.CONSUME(GtEq) },
2575
+ { ALT: () => this.CONSUME(Lt) },
2576
+ { ALT: () => this.CONSUME(Gt) }
2577
+ ]);
2578
+ this.SUBRULE(this.operand);
2579
+ } },
2580
+ { ALT: () => {
2581
+ this.CONSUME(Like);
2582
+ this.SUBRULE2(this.operand);
2583
+ this.OPTION({
2584
+ GATE: () => this.LA(1).tokenType === Identifier && this.LA(1).image.toLowerCase() === "escape",
2585
+ DEF: () => {
2586
+ this.CONSUME(Identifier);
2587
+ this.SUBRULE3(this.operand);
2588
+ }
2589
+ });
2590
+ } },
2591
+ { ALT: () => {
2592
+ this.CONSUME(Between);
2593
+ this.SUBRULE4(this.operand);
2594
+ this.CONSUME(And);
2595
+ this.SUBRULE5(this.operand);
2596
+ } },
2597
+ { ALT: () => {
2598
+ this.CONSUME(In);
2599
+ this.OR3([{ ALT: () => {
2600
+ this.CONSUME(LParen);
2601
+ this.OPTION2(() => {
2602
+ this.SUBRULE6(this.operand);
2603
+ this.MANY(() => {
2604
+ this.CONSUME(Comma);
2605
+ this.SUBRULE7(this.operand);
2606
+ });
2607
+ });
2608
+ this.CONSUME(RParen);
2609
+ } }, {
2610
+ GATE: () => this.LA(1).tokenType !== LParen,
2611
+ ALT: () => this.SUBRULE8(this.operand)
2612
+ }]);
2613
+ } },
2614
+ {
2615
+ GATE: () => this.isIsPredicate(),
2616
+ ALT: () => {
2617
+ this.CONSUME2(Identifier);
2618
+ this.OPTION3(() => this.CONSUME(Not));
2619
+ this.OR4([{ ALT: () => this.CONSUME(Null) }, { ALT: () => this.CONSUME3(Identifier) }]);
2620
+ }
2621
+ }
787
2622
  ]);
788
2623
  });
2624
+ /** Cheap lookahead: `is` — the `is null` / `is initial` predicate. */
2625
+ isIsPredicate() {
2626
+ const first = this.LA(1);
2627
+ if (!first) return false;
2628
+ return first.tokenType?.name === "Identifier" && first.image.toLowerCase() === "is";
2629
+ }
2630
+ /**
2631
+ * Relaxed expression — operands with optional predicates and AND/OR
2632
+ * chains. Used for call arguments and `case … when` branches, where a
2633
+ * bare operand is legal.
2634
+ */
789
2635
  expression = this.RULE("expression", () => {
790
2636
  this.SUBRULE(this.comparison);
791
2637
  this.MANY(() => {
@@ -796,23 +2642,204 @@ var CdsParser = class extends CstParser {
796
2642
  comparison = this.RULE("comparison", () => {
797
2643
  this.SUBRULE(this.operand);
798
2644
  this.OPTION(() => {
2645
+ this.SUBRULE(this.predicate);
2646
+ });
2647
+ });
2648
+ /**
2649
+ * An operand — a primary expression optionally chained with arithmetic
2650
+ * operators: `a.b`, `f(x)`, `a + b * c`, `case … end`, `cast(…)`.
2651
+ */
2652
+ operand = this.RULE("operand", () => {
2653
+ this.SUBRULE(this.primary);
2654
+ this.MANY(() => {
799
2655
  this.OR([
800
- { ALT: () => this.CONSUME(EqEq) },
801
- { ALT: () => this.CONSUME(Eq) },
802
- { ALT: () => this.CONSUME(NotEq) },
803
- { ALT: () => this.CONSUME(LtEq) },
804
- { ALT: () => this.CONSUME(GtEq) },
805
- { ALT: () => this.CONSUME(Lt) },
806
- { ALT: () => this.CONSUME(Gt) }
2656
+ { ALT: () => this.CONSUME(Plus) },
2657
+ { ALT: () => this.CONSUME(Minus) },
2658
+ { ALT: () => this.CONSUME(Star) },
2659
+ { ALT: () => this.CONSUME(Slash) }
807
2660
  ]);
2661
+ this.SUBRULE2(this.primary);
2662
+ });
2663
+ });
2664
+ primary = this.RULE("primary", () => {
2665
+ this.OPTION(() => {
2666
+ this.OR([{ ALT: () => this.CONSUME(Minus) }, { ALT: () => this.CONSUME(Plus) }]);
2667
+ });
2668
+ this.OR2([
2669
+ { ALT: () => this.SUBRULE(this.parenthesizedExpression) },
2670
+ {
2671
+ GATE: () => this.isCaseExpression(),
2672
+ ALT: () => this.SUBRULE(this.caseExpression)
2673
+ },
2674
+ {
2675
+ GATE: () => this.isCastExpression(),
2676
+ ALT: () => this.SUBRULE(this.castExpression)
2677
+ },
2678
+ { ALT: () => this.SUBRULE(this.systemVariable) },
2679
+ { ALT: () => this.SUBRULE(this.parameterRef) },
2680
+ {
2681
+ GATE: () => this.isLiteralStart(),
2682
+ ALT: () => this.SUBRULE(this.literal)
2683
+ },
2684
+ { ALT: () => this.SUBRULE(this.pathOperand) }
2685
+ ]);
2686
+ });
2687
+ /**
2688
+ * A path or call operand: `<name>[<attr>].<name>…` optionally followed by
2689
+ * an argument list and a window `over (…)`.
2690
+ * - `a.b`, `_assoc.field`, `_a[to exact one: f = 1].g`
2691
+ * - `coalesce(a, b)`, `sum(x)`, `count(*)`, `count(distinct x)`
2692
+ * - `row_number() over (partition by a order by b descending)`
2693
+ */
2694
+ pathOperand = this.RULE("pathOperand", () => {
2695
+ this.SUBRULE(this.pathSegment);
2696
+ this.MANY(() => {
2697
+ this.CONSUME(Dot);
2698
+ this.SUBRULE2(this.pathSegment);
2699
+ });
2700
+ this.OPTION(() => {
2701
+ this.CONSUME(LParen);
2702
+ this.OPTION2(() => {
2703
+ this.SUBRULE(this.callArgument);
2704
+ this.MANY2(() => {
2705
+ this.CONSUME(Comma);
2706
+ this.SUBRULE2(this.callArgument);
2707
+ });
2708
+ });
2709
+ this.CONSUME(RParen);
2710
+ this.OPTION3(() => this.SUBRULE(this.overClause));
2711
+ });
2712
+ });
2713
+ /** One path segment: `<name>` plus optional `[…]` attributes. */
2714
+ pathSegment = this.RULE("pathSegment", () => {
2715
+ this.SUBRULE(this.cdsName);
2716
+ this.MANY(() => {
2717
+ this.SUBRULE(this.bracketTokens);
2718
+ });
2719
+ });
2720
+ /**
2721
+ * `[ … ]` — square-bracketed path attribute or association filter.
2722
+ * Contents (`cardinality`, `inner|left outer`, `where <cond>`,
2723
+ * `<card>:<cond>`, parameter bindings) are captured opaquely.
2724
+ */
2725
+ bracketTokens = this.RULE("bracketTokens", () => {
2726
+ this.CONSUME(LBracket);
2727
+ this.MANY(() => {
2728
+ this.OR(allTokens.filter((t) => ![
2729
+ "WhiteSpace",
2730
+ "LineComment",
2731
+ "BlockComment",
2732
+ "SqlLineComment",
2733
+ "LBracket",
2734
+ "RBracket"
2735
+ ].includes(t.name)).map((tokenType) => ({ ALT: () => this.CONSUME(tokenType) })));
2736
+ });
2737
+ this.CONSUME(RBracket);
2738
+ });
2739
+ /**
2740
+ * One call argument: `*`, `distinct <operand>` or a relaxed expression
2741
+ * (bare operands are legal inside argument lists).
2742
+ */
2743
+ callArgument = this.RULE("callArgument", () => {
2744
+ this.OR([
2745
+ { ALT: () => this.CONSUME(Star) },
2746
+ {
2747
+ GATE: () => this.LA(1).tokenType === Distinct && this.isElementStart(this.LA(2)),
2748
+ ALT: () => {
2749
+ this.CONSUME(Distinct);
2750
+ this.SUBRULE(this.operand);
2751
+ }
2752
+ },
2753
+ { ALT: () => this.SUBRULE(this.expression) }
2754
+ ]);
2755
+ });
2756
+ /**
2757
+ * `over (partition by <fields> [order by <sort items>])` — window function
2758
+ * clause.
2759
+ */
2760
+ overClause = this.RULE("overClause", () => {
2761
+ this.CONSUME(Over);
2762
+ this.CONSUME(LParen);
2763
+ this.OPTION(() => {
2764
+ this.CONSUME(Partition);
2765
+ this.CONSUME(By);
2766
+ this.SUBRULE(this.qualifiedName);
2767
+ this.MANY(() => {
2768
+ this.CONSUME(Comma);
2769
+ this.SUBRULE2(this.qualifiedName);
2770
+ });
2771
+ });
2772
+ this.OPTION2(() => {
2773
+ this.CONSUME(Order);
2774
+ this.CONSUME2(By);
2775
+ this.SUBRULE(this.sortItem);
2776
+ this.MANY2(() => {
2777
+ this.CONSUME2(Comma);
2778
+ this.SUBRULE2(this.sortItem);
2779
+ });
2780
+ });
2781
+ this.CONSUME(RParen);
2782
+ });
2783
+ /** `( <expression> )` — a parenthesized sub-expression. */
2784
+ parenthesizedExpression = this.RULE("parenthesizedExpression", () => {
2785
+ this.CONSUME(LParen);
2786
+ this.SUBRULE(this.expression);
2787
+ this.CONSUME(RParen);
2788
+ });
2789
+ /** `cast( <value> as <type> [preserving type] )` */
2790
+ castExpression = this.RULE("castExpression", () => {
2791
+ this.CONSUME(Identifier);
2792
+ this.CONSUME(LParen);
2793
+ this.SUBRULE(this.operand);
2794
+ this.CONSUME(As);
2795
+ this.SUBRULE(this.typeReference);
2796
+ this.OPTION({
2797
+ GATE: () => this.LA(1).tokenType === Identifier && this.LA(1).image.toLowerCase() === "preserving",
2798
+ DEF: () => {
2799
+ this.CONSUME2(Identifier);
2800
+ this.CONSUME(Type);
2801
+ }
2802
+ });
2803
+ this.CONSUME(RParen);
2804
+ });
2805
+ /**
2806
+ * `case [operand] when … then … [else …|else null] end`
2807
+ * — simple case: `case f when 'a' then 1 …`
2808
+ * — searched case: `case when f = 1 then 'x' …`
2809
+ */
2810
+ caseExpression = this.RULE("caseExpression", () => {
2811
+ this.CONSUME(Case);
2812
+ this.OPTION({
2813
+ GATE: () => this.LA(1).tokenType !== When,
2814
+ DEF: () => this.SUBRULE(this.operand)
2815
+ });
2816
+ this.AT_LEAST_ONE(() => {
2817
+ this.CONSUME(When);
2818
+ this.SUBRULE(this.expression);
2819
+ this.CONSUME(Then);
808
2820
  this.SUBRULE2(this.operand);
809
2821
  });
2822
+ this.OPTION2(() => {
2823
+ this.CONSUME(Else);
2824
+ this.SUBRULE3(this.operand);
2825
+ });
2826
+ this.CONSUME(End);
810
2827
  });
811
- operand = this.RULE("operand", () => {
812
- this.OR([{ ALT: () => this.SUBRULE(this.literal) }, { ALT: () => this.SUBRULE(this.qualifiedName) }]);
2828
+ /** `$session.client`, `$parameters.P_X`, `$projection.Field`, `$node.x`. */
2829
+ systemVariable = this.RULE("systemVariable", () => {
2830
+ this.CONSUME(Dollar);
2831
+ this.SUBRULE(this.qualifiedName);
2832
+ });
2833
+ /** `:P_Param` — a parameter reference of the consuming view. */
2834
+ parameterRef = this.RULE("parameterRef", () => {
2835
+ this.CONSUME(Colon);
2836
+ this.SUBRULE(this.qualifiedName);
813
2837
  });
814
2838
  typeReference = this.RULE("typeReference", () => {
815
- this.OR([{ ALT: () => this.SUBRULE(this.builtinType) }, { ALT: () => this.SUBRULE(this.namedType) }]);
2839
+ this.OR([{
2840
+ GATE: () => this.LA(1).tokenType === Abap && this.LA(2).tokenType === Dot,
2841
+ ALT: () => this.SUBRULE(this.builtinType)
2842
+ }, { ALT: () => this.SUBRULE(this.namedType) }]);
816
2843
  });
817
2844
  /** `abap.char(10)` or `abap.dec(11,2)` */
818
2845
  builtinType = this.RULE("builtinType", () => {
@@ -835,35 +2862,16 @@ var CdsParser = class extends CstParser {
835
2862
  });
836
2863
  /** An identifier that may also be a keyword used as a name. */
837
2864
  cdsName = this.RULE("cdsName", () => {
838
- this.OR([
839
- { ALT: () => this.CONSUME(Identifier) },
840
- { ALT: () => this.CONSUME(Table) },
841
- { ALT: () => this.CONSUME(Structure) },
842
- { ALT: () => this.CONSUME(Type) },
843
- { ALT: () => this.CONSUME(Service) },
844
- { ALT: () => this.CONSUME(Entity) },
845
- { ALT: () => this.CONSUME(Key) },
846
- { ALT: () => this.CONSUME(Expose) },
847
- { ALT: () => this.CONSUME(View) },
848
- { ALT: () => this.CONSUME(Role) },
849
- { ALT: () => this.CONSUME(Association) },
850
- { ALT: () => this.CONSUME(Composition) },
851
- { ALT: () => this.CONSUME(Abstract) },
852
- { ALT: () => this.CONSUME(Custom) },
853
- { ALT: () => this.CONSUME(Virtual) },
854
- { ALT: () => this.CONSUME(Redirected) },
855
- { ALT: () => this.CONSUME(Default) },
856
- { ALT: () => this.CONSUME(Parameters) },
857
- { ALT: () => this.CONSUME(Grant) },
858
- { ALT: () => this.CONSUME(Many) },
859
- { ALT: () => this.CONSUME(One) },
860
- { ALT: () => this.CONSUME(Of) },
861
- { ALT: () => this.CONSUME(Projection) },
862
- { ALT: () => this.CONSUME(Select) },
863
- { ALT: () => this.CONSUME(From) },
864
- { ALT: () => this.CONSUME(To) },
865
- { ALT: () => this.CONSUME(Where) }
866
- ]);
2865
+ this.OR(CdsParser.NAME_TOKENS.map((tokenType) => ({ ALT: () => this.CONSUME(tokenType) })));
2866
+ });
2867
+ /**
2868
+ * A name usable as an *implicit* (bare) source alias.
2869
+ *
2870
+ * It is `cdsName` without the FROM-region starters — those tokens belong
2871
+ * to the next item of the FROM region and must not be read as an alias.
2872
+ */
2873
+ bareAliasName = this.RULE("bareAliasName", () => {
2874
+ this.OR(CdsParser.NAME_TOKENS.filter((tokenType) => !CdsParser.FROM_REGION_STARTERS.has(tokenType)).map((tokenType) => ({ ALT: () => this.CONSUME(tokenType) })));
867
2875
  });
868
2876
  /** Dot-separated qualified name: foo.bar.baz */
869
2877
  qualifiedName = this.RULE("qualifiedName", () => {
@@ -897,13 +2905,54 @@ function collectTokenImages(node) {
897
2905
  out.sort((a, b) => (a.startOffset ?? 0) - (b.startOffset ?? 0));
898
2906
  return out;
899
2907
  }
2908
+ /** All tokens of a rule context, in source order. */
2909
+ function ctxTokens(ctx) {
2910
+ const out = [];
2911
+ for (const value of Object.values(ctx)) for (const child of value ?? []) if ("tokenType" in child) out.push(child);
2912
+ else out.push(...collectTokenImages(child));
2913
+ out.sort((a, b) => (a.startOffset ?? 0) - (b.startOffset ?? 0));
2914
+ return out;
2915
+ }
2916
+ /**
2917
+ * Format tokens as source-like text: no space before `(` `,` `)` `]` `.`,
2918
+ * and none after `(` `[` `.` `$` `:` `@` — e.g. `coalesce(a.b, c)`,
2919
+ * `$session.client`, `:P_Param`, `@Anno.value`.
2920
+ */
2921
+ function formatTokens(toks) {
2922
+ let out = "";
2923
+ for (let i = 0; i < toks.length; i += 1) {
2924
+ const img = toks[i].image;
2925
+ if (!out) {
2926
+ out = img;
2927
+ continue;
2928
+ }
2929
+ const tight = img.startsWith("'") && i >= 2 && toks[i - 2].image === "." && /^[A-Za-z_]\w*$/.test(toks[i - 1].image) || [
2930
+ "(",
2931
+ ",",
2932
+ ")",
2933
+ "]",
2934
+ ".",
2935
+ ":"
2936
+ ].includes(img) || out.endsWith("(") || out.endsWith("[") || out.endsWith(".") || out.endsWith("$") || out.endsWith(":") || out.endsWith("@");
2937
+ out += tight ? img : ` ${img}`;
2938
+ }
2939
+ return out;
2940
+ }
900
2941
  function toExpression(node) {
901
2942
  const toks = collectTokenImages(node);
902
2943
  return {
903
- source: toks.map((t) => t.image).join(" "),
2944
+ source: formatTokens(toks),
904
2945
  tokens: toks.map((t) => t.image)
905
2946
  };
906
2947
  }
2948
+ /**
2949
+ * Raw text of a CST node — the token images concatenated without separators.
2950
+ * Used for parameter bindings, where the original spelling matters
2951
+ * (`:P_Param`, `$session.client`, `'X'`).
2952
+ */
2953
+ function toRawText(node) {
2954
+ return collectTokenImages(node).map((t) => t.image).join("");
2955
+ }
907
2956
  var CdsVisitor = class extends BaseCstVisitor {
908
2957
  constructor() {
909
2958
  super();
@@ -917,20 +2966,28 @@ var CdsVisitor = class extends BaseCstVisitor {
917
2966
  }
918
2967
  definition(ctx) {
919
2968
  if (ctx.defineStatement) return this.visit(asNodes(ctx.defineStatement)[0]);
2969
+ if (ctx.extendStatement) return this.visit(asNodes(ctx.extendStatement)[0]);
920
2970
  return this.visit(asNodes(ctx.annotateStatement)[0]);
921
2971
  }
922
2972
  defineStatement(ctx) {
923
2973
  const dispatch = [
924
- ["tableDefinition", asNodes(ctx.tableDefinition)[0]],
925
- ["structureDefinition", asNodes(ctx.structureDefinition)[0]],
926
- ["viewEntityDefinition", asNodes(ctx.viewEntityDefinition)[0]],
927
- ["abstractEntityDefinition", asNodes(ctx.abstractEntityDefinition)[0]],
928
- ["customEntityDefinition", asNodes(ctx.customEntityDefinition)[0]],
929
- ["simpleTypeDefinition", asNodes(ctx.simpleTypeDefinition)[0]],
930
- ["serviceDefinition", asNodes(ctx.serviceDefinition)[0]],
931
- ["roleDefinition", asNodes(ctx.roleDefinition)[0]]
2974
+ asNodes(ctx.prefixedDefinition)[0],
2975
+ asNodes(ctx.tableDefinition)[0],
2976
+ asNodes(ctx.structureDefinition)[0],
2977
+ asNodes(ctx.viewEntityDefinition)[0],
2978
+ asNodes(ctx.abstractEntityDefinition)[0],
2979
+ asNodes(ctx.customEntityDefinition)[0],
2980
+ asNodes(ctx.simpleTypeDefinition)[0],
2981
+ asNodes(ctx.serviceDefinition)[0],
2982
+ asNodes(ctx.roleDefinition)[0],
2983
+ asNodes(ctx.hierarchyDefinition)[0],
2984
+ asNodes(ctx.aspectDefinition)[0],
2985
+ asNodes(ctx.externalEntityDefinition)[0],
2986
+ asNodes(ctx.scalarFunctionDefinition)[0],
2987
+ asNodes(ctx.annotationDefinition)[0],
2988
+ asNodes(ctx.accessPolicyDefinition)[0]
932
2989
  ];
933
- for (const [, node] of dispatch) if (node) return this.visit(node);
2990
+ for (const node of dispatch) if (node) return this.visit(node);
934
2991
  return {
935
2992
  kind: "table",
936
2993
  name: "",
@@ -938,14 +2995,56 @@ var CdsVisitor = class extends BaseCstVisitor {
938
2995
  members: []
939
2996
  };
940
2997
  }
941
- tableDefinition(ctx) {
2998
+ /**
2999
+ * `define (writable|root)+ <view|external entity|table entity>` — applies
3000
+ * the leading modifiers to whichever entity form followed them.
3001
+ */
3002
+ prefixedDefinition(ctx) {
3003
+ const writable = !!asTokens(ctx.Writable).length;
3004
+ const root = !!asTokens(ctx.Root).length;
3005
+ const node = asNodes(ctx.viewEntityDefinition)[0] ?? asNodes(ctx.externalEntityDefinition)[0] ?? asNodes(ctx.rootTableEntityDefinition)[0];
3006
+ const def = this.visit(node);
3007
+ if (root) def.root = true;
3008
+ if (writable) def.writable = true;
3009
+ return def;
3010
+ }
3011
+ rootTableEntityDefinition(ctx) {
942
3012
  return {
943
3013
  kind: "table",
944
3014
  name: this.visit(asNodes(ctx.cdsName)[0]),
945
3015
  annotations: [],
3016
+ entity: true,
946
3017
  members: asNodes(ctx.tableMember).map((node) => this.visit(node))
947
3018
  };
948
3019
  }
3020
+ tableDefinition(ctx) {
3021
+ if (ctx.tableFunctionTail) return this.visit(asNodes(ctx.tableFunctionTail)[0]);
3022
+ const name = this.visit(asNodes(ctx.cdsName)[0]);
3023
+ const members = asNodes(ctx.tableMember).map((node) => this.visit(node));
3024
+ return {
3025
+ kind: "table",
3026
+ name,
3027
+ annotations: [],
3028
+ ...!!asTokens(ctx.Entity).length && { entity: true },
3029
+ members
3030
+ };
3031
+ }
3032
+ tableFunctionTail(ctx) {
3033
+ const name = this.visit(asNodes(ctx.cdsName)[0]);
3034
+ const parameters = ctx.parametersClause ? this.visit(asNodes(ctx.parametersClause)[0]) : [];
3035
+ const members = asNodes(ctx.fieldDefinition).map((node) => this.visit(node));
3036
+ const implementingClass = this.visit(asNodes(ctx.qualifiedName)[0]);
3037
+ const implementingMethod = asNodes(ctx.cdsName)[1] ? this.visit(asNodes(ctx.cdsName)[1]) : void 0;
3038
+ return {
3039
+ kind: "tableFunction",
3040
+ name,
3041
+ annotations: [],
3042
+ parameters,
3043
+ members,
3044
+ implementingClass,
3045
+ ...implementingMethod && { implementingMethod }
3046
+ };
3047
+ }
949
3048
  structureDefinition(ctx) {
950
3049
  return {
951
3050
  kind: "structure",
@@ -955,61 +3054,259 @@ var CdsVisitor = class extends BaseCstVisitor {
955
3054
  };
956
3055
  }
957
3056
  viewEntityDefinition(ctx) {
958
- const name = this.visit(asNodes(ctx.cdsName)[0]);
3057
+ const legacy = asTokens(ctx.Entity).length === 0;
3058
+ const names = asNodes(ctx.cdsName).map((n) => this.visit(n));
3059
+ const name = names[0] ?? "";
3060
+ const providerContract = ctx.Contract ? names[1] : void 0;
959
3061
  const parameters = ctx.parametersClause ? this.visit(asNodes(ctx.parametersClause)[0]) : [];
960
- let sourceKind = "select";
961
- let source = { name: "" };
962
- if (ctx.selectFromClause) {
963
- sourceKind = "select";
964
- source = this.visit(asNodes(ctx.selectFromClause)[0]);
965
- } else if (ctx.projectionOnClause) {
966
- sourceKind = "projection";
967
- source = this.visit(asNodes(ctx.projectionOnClause)[0]);
968
- }
969
- const members = ctx.elementList ? this.visit(asNodes(ctx.elementList)[0]) : [];
970
- const where = ctx.expression ? toExpression(asNodes(ctx.expression)[0]) : void 0;
3062
+ const tail = this.viewTail(ctx);
971
3063
  return {
972
3064
  kind: "viewEntity",
973
3065
  name,
974
3066
  annotations: [],
975
- sourceKind,
3067
+ sourceKind: tail.sourceKind,
3068
+ source: tail.source,
3069
+ joins: tail.joins,
3070
+ parameters,
3071
+ members: tail.members,
3072
+ ...legacy && { legacy: true },
3073
+ ...tail.distinct && { distinct: true },
3074
+ ...providerContract && { providerContract },
3075
+ ...tail.fromAssociations.length > 0 && { fromAssociations: tail.fromAssociations },
3076
+ ...tail.redefinedAssociations.length > 0 && { redefinedAssociations: tail.redefinedAssociations },
3077
+ ...tail.aspectBindings.length > 0 && { aspectBindings: tail.aspectBindings },
3078
+ ...tail.where && { where: tail.where },
3079
+ ...tail.groupBy && { groupBy: tail.groupBy },
3080
+ ...tail.having && { having: tail.having },
3081
+ ...tail.setOperations && tail.setOperations.length > 0 && { setOperations: tail.setOperations }
3082
+ };
3083
+ }
3084
+ /** Reduce the `as select …` / `as projection …` tail to a shared shape. */
3085
+ viewTail(ctx) {
3086
+ const empty = {
3087
+ sourceKind: "select",
3088
+ distinct: false,
3089
+ source: { name: "" },
3090
+ joins: [],
3091
+ fromAssociations: [],
3092
+ redefinedAssociations: [],
3093
+ aspectBindings: [],
3094
+ members: []
3095
+ };
3096
+ if (ctx.selectStatement) {
3097
+ const select = this.visit(asNodes(ctx.selectStatement)[0]);
3098
+ return {
3099
+ ...empty,
3100
+ sourceKind: "select",
3101
+ distinct: !!select.distinct,
3102
+ source: select.source,
3103
+ joins: select.joins,
3104
+ fromAssociations: select.fromAssociations ?? [],
3105
+ aspectBindings: select.aspectBindings ?? [],
3106
+ members: select.members,
3107
+ where: select.where,
3108
+ groupBy: select.groupBy,
3109
+ having: select.having,
3110
+ setOperations: select.setOperations
3111
+ };
3112
+ }
3113
+ if (ctx.projectionTail) {
3114
+ const proj = this.visit(asNodes(ctx.projectionTail)[0]);
3115
+ return {
3116
+ ...empty,
3117
+ sourceKind: "projection",
3118
+ source: proj.source,
3119
+ fromAssociations: proj.associations,
3120
+ redefinedAssociations: proj.redefinedAssociations,
3121
+ aspectBindings: proj.aspectBindings,
3122
+ members: proj.members,
3123
+ where: proj.where
3124
+ };
3125
+ }
3126
+ return empty;
3127
+ }
3128
+ abstractEntityDefinition(ctx) {
3129
+ return {
3130
+ kind: "abstractEntity",
3131
+ name: this.visit(asNodes(ctx.cdsName)[0]),
3132
+ annotations: [],
3133
+ parameters: ctx.parametersClause ? this.visit(asNodes(ctx.parametersClause)[0]) : [],
3134
+ members: this.visit(asNodes(ctx.elementList)[0])
3135
+ };
3136
+ }
3137
+ customEntityDefinition(ctx) {
3138
+ return {
3139
+ kind: "customEntity",
3140
+ name: this.visit(asNodes(ctx.cdsName)[0]),
3141
+ annotations: [],
3142
+ parameters: ctx.parametersClause ? this.visit(asNodes(ctx.parametersClause)[0]) : [],
3143
+ members: this.visit(asNodes(ctx.elementList)[0])
3144
+ };
3145
+ }
3146
+ externalEntityDefinition(ctx) {
3147
+ const names = asNodes(ctx.cdsName).map((n) => this.visit(n));
3148
+ const name = names[0] ?? "";
3149
+ const externalName = ctx.Name ? names[1] : void 0;
3150
+ return {
3151
+ kind: "externalEntity",
3152
+ name,
3153
+ annotations: [],
3154
+ parameters: ctx.parametersClause ? this.visit(asNodes(ctx.parametersClause)[0]) : [],
3155
+ members: asNodes(ctx.fieldDefinition).map((node) => this.visit(node)),
3156
+ ...externalName && { externalName }
3157
+ };
3158
+ }
3159
+ selectStatement(ctx) {
3160
+ const distinct = !!asTokens(ctx.Distinct).length;
3161
+ const source = this.visit(asNodes(ctx.dataSource)[0]);
3162
+ const joins = asNodes(ctx.joinClause).map((node) => this.visit(node));
3163
+ const fromAssociations = asNodes(ctx.associationTail).map((node) => this.visit(node));
3164
+ const aspectBindings = asNodes(ctx.bindAspect).map((node) => this.visit(node));
3165
+ const members = ctx.elementList ? this.visit(asNodes(ctx.elementList)[0]) : [];
3166
+ const clauses = ctx.selectClauses ? this.visit(asNodes(ctx.selectClauses)[0]) : {};
3167
+ return {
3168
+ kind: "selectStatement",
3169
+ ...distinct && { distinct: true },
3170
+ source,
3171
+ joins,
3172
+ ...fromAssociations.length > 0 && { fromAssociations },
3173
+ ...aspectBindings.length > 0 && { aspectBindings },
3174
+ members,
3175
+ ...clauses.where && { where: clauses.where },
3176
+ ...clauses.groupBy && { groupBy: clauses.groupBy },
3177
+ ...clauses.having && { having: clauses.having },
3178
+ ...clauses.setOperations && clauses.setOperations.length > 0 && { setOperations: clauses.setOperations }
3179
+ };
3180
+ }
3181
+ selectClauses(ctx) {
3182
+ const conditions = asNodes(ctx.condition);
3183
+ const groupBy = asNodes(ctx.qualifiedName).map((n) => this.visit(n));
3184
+ const where = ctx.Where ? toExpression(conditions[0]) : void 0;
3185
+ let having;
3186
+ if (ctx.Having) having = toExpression(conditions[ctx.Where ? 1 : 0]);
3187
+ const setOperations = asNodes(ctx.setOperation).map((n) => this.visit(n));
3188
+ return {
3189
+ ...where && { where },
3190
+ ...groupBy.length > 0 && { groupBy },
3191
+ ...having && { having },
3192
+ ...setOperations.length > 0 && { setOperations }
3193
+ };
3194
+ }
3195
+ setOperation(ctx) {
3196
+ const statement = this.visit(asNodes(ctx.selectStatement)[0]);
3197
+ return {
3198
+ kind: "setOperation",
3199
+ operator: this.setOperator(ctx),
3200
+ statement
3201
+ };
3202
+ }
3203
+ /** Shared `union [all]|intersect|except` operator mapping. */
3204
+ setOperator(ctx) {
3205
+ if (ctx.Intersect) return "intersect";
3206
+ if (ctx.Except) return "except";
3207
+ return asTokens(ctx.All).length ? "unionAll" : "union";
3208
+ }
3209
+ /** Visit `ctx[key][0]` when the optional subrule fired, else `undefined`. */
3210
+ optional(ctx, key) {
3211
+ const nodes = asNodes(ctx[key]);
3212
+ return nodes.length ? this.visit(nodes[0]) : void 0;
3213
+ }
3214
+ projectionTail(ctx) {
3215
+ const source = this.visit(asNodes(ctx.dataSource)[0]);
3216
+ const associations = asNodes(ctx.associationTail).map((node) => this.visit(node));
3217
+ const redefinedAssociations = asNodes(ctx.redefinedAssociation).map((node) => this.visit(node));
3218
+ const aspectBindings = asNodes(ctx.bindAspect).map((node) => this.visit(node));
3219
+ const members = ctx.elementList ? this.visit(asNodes(ctx.elementList)[0]) : [];
3220
+ const where = ctx.condition ? toExpression(asNodes(ctx.condition)[0]) : void 0;
3221
+ return {
976
3222
  source,
977
- parameters,
3223
+ associations,
3224
+ redefinedAssociations,
3225
+ aspectBindings,
978
3226
  members,
979
3227
  ...where && { where }
980
3228
  };
981
3229
  }
982
- abstractEntityDefinition(ctx) {
3230
+ dataSource(ctx) {
3231
+ const name = this.visit(asNodes(ctx.dataSourcePath)[0]);
3232
+ const aliasNode = asNodes(ctx.cdsName)[0] ?? asNodes(ctx.bareAliasName)[0];
3233
+ const alias = aliasNode ? this.visit(aliasNode) : void 0;
3234
+ const parameters = ctx.dataSourceParameters ? this.visit(asNodes(ctx.dataSourceParameters)[0]) : void 0;
983
3235
  return {
984
- kind: "abstractEntity",
985
- name: this.visit(asNodes(ctx.cdsName)[0]),
986
- annotations: [],
987
- parameters: ctx.parametersClause ? this.visit(asNodes(ctx.parametersClause)[0]) : [],
988
- members: this.visit(asNodes(ctx.elementList)[0])
3236
+ name,
3237
+ ...alias && { alias },
3238
+ ...parameters && { parameters }
989
3239
  };
990
3240
  }
991
- customEntityDefinition(ctx) {
3241
+ /** `<seg>[<attr>].<seg>…` — data-source path with raw bracket text kept. */
3242
+ dataSourcePath(ctx) {
3243
+ return asNodes(ctx.pathSegment).map((segment) => {
3244
+ const segCtx = segment.children;
3245
+ return this.visit(asNodes(segCtx.cdsName)[0]) + asNodes(segCtx.bracketTokens).map((b) => toRawText(b)).join("");
3246
+ }).join(".");
3247
+ }
3248
+ dataSourceParameters(ctx) {
3249
+ return asNodes(ctx.dataSourceParameter).map((node) => this.visit(node));
3250
+ }
3251
+ dataSourceParameter(ctx) {
992
3252
  return {
993
- kind: "customEntity",
994
- name: this.visit(asNodes(ctx.cdsName)[0]),
995
- annotations: [],
996
- parameters: ctx.parametersClause ? this.visit(asNodes(ctx.parametersClause)[0]) : [],
997
- members: this.visit(asNodes(ctx.elementList)[0])
3253
+ name: this.visit(asNodes(ctx.qualifiedName)[0]),
3254
+ value: toRawText(asNodes(ctx.parameterBinding)[0])
998
3255
  };
999
3256
  }
1000
- selectFromClause(ctx) {
1001
- return this.visit(asNodes(ctx.dataSource)[0]);
3257
+ parameterBinding() {
3258
+ return null;
3259
+ }
3260
+ joinClause(ctx) {
3261
+ if (ctx.Cross) return {
3262
+ kind: "join",
3263
+ joinType: "cross",
3264
+ source: this.visit(asNodes(ctx.joinOperand)[0])
3265
+ };
3266
+ const prefix = this.visit(asNodes(ctx.joinPrefix)[0]);
3267
+ const source = this.visit(asNodes(ctx.joinOperand)[0]);
3268
+ const on = toExpression(asNodes(ctx.condition)[0]);
3269
+ return {
3270
+ kind: "join",
3271
+ joinType: prefix.joinType,
3272
+ source,
3273
+ on,
3274
+ ...prefix.cardinality && { cardinality: prefix.cardinality }
3275
+ };
3276
+ }
3277
+ joinPrefix(ctx) {
3278
+ let joinType = "inner";
3279
+ if (asTokens(ctx.Full).length) joinType = "fullOuter";
3280
+ else if (asTokens(ctx.Left).length) joinType = "leftOuter";
3281
+ else if (asTokens(ctx.Right).length) joinType = "rightOuter";
3282
+ const cardinality = ctx.joinCardinality ? this.visit(asNodes(ctx.joinCardinality)[0]) : void 0;
3283
+ return {
3284
+ joinType,
3285
+ ...cardinality && { cardinality }
3286
+ };
3287
+ }
3288
+ joinCardinality(ctx) {
3289
+ const words = asNodes(ctx.cardinalWord).map((n) => this.visit(n));
3290
+ if (words.length === 2) return {
3291
+ source: words[0],
3292
+ target: words[1]
3293
+ };
3294
+ return { target: words[0] };
3295
+ }
3296
+ cardinalWord(ctx) {
3297
+ if (asTokens(ctx.Exact).length) return "exactOne";
3298
+ if (asTokens(ctx.Many).length) return "many";
3299
+ return "one";
1002
3300
  }
1003
- projectionOnClause(ctx) {
3301
+ joinOperand(ctx) {
3302
+ if (ctx.joinGroup) return this.visit(asNodes(ctx.joinGroup)[0]);
1004
3303
  return this.visit(asNodes(ctx.dataSource)[0]);
1005
3304
  }
1006
- dataSource(ctx) {
1007
- const name = this.visit(asNodes(ctx.qualifiedName)[0]);
1008
- const aliasNode = asNodes(ctx.cdsName)[0];
1009
- const alias = aliasNode ? this.visit(aliasNode) : void 0;
3305
+ joinGroup(ctx) {
1010
3306
  return {
1011
- name,
1012
- ...alias && { alias }
3307
+ kind: "joinGroup",
3308
+ source: this.visit(asNodes(ctx.joinOperand)[0]),
3309
+ joins: asNodes(ctx.joinClause).map((node) => this.visit(node))
1013
3310
  };
1014
3311
  }
1015
3312
  elementList(ctx) {
@@ -1042,14 +3339,16 @@ var CdsVisitor = class extends BaseCstVisitor {
1042
3339
  notNull: tail.notNull
1043
3340
  };
1044
3341
  }
1045
- const proj = this.visit(asNodes(ctx.projectionExpression)[0]);
3342
+ const proj = this.visit(asNodes(ctx.elementExpression)[0]);
1046
3343
  return {
1047
3344
  annotations: [],
1048
3345
  isKey,
1049
3346
  isVirtual,
1050
3347
  isRedirected,
1051
3348
  expression: proj.expression,
1052
- ...proj.alias && { alias: proj.alias }
3349
+ ...proj.alias && { alias: proj.alias },
3350
+ ...proj.localized && { localized: true },
3351
+ ...proj.redirectedTo && { redirectedTo: proj.redirectedTo }
1053
3352
  };
1054
3353
  }
1055
3354
  typedFieldTail(ctx) {
@@ -1059,36 +3358,114 @@ var CdsVisitor = class extends BaseCstVisitor {
1059
3358
  notNull: !!asTokens(ctx.Not).length
1060
3359
  };
1061
3360
  }
1062
- projectionExpression(ctx) {
1063
- const expression = this.visit(asNodes(ctx.qualifiedName)[0]);
3361
+ elementExpression(ctx) {
3362
+ const expression = formatTokens(collectTokenImages(asNodes(ctx.operand)[0]));
1064
3363
  const aliasNode = asNodes(ctx.cdsName)[0];
1065
3364
  const alias = aliasNode ? this.visit(aliasNode) : void 0;
3365
+ const localized = !!asTokens(ctx.Localized).length;
3366
+ const redirectedTo = ctx.redirection ? this.visit(asNodes(ctx.redirection)[0]) : void 0;
1066
3367
  return {
1067
3368
  expression,
3369
+ ...alias && { alias },
3370
+ ...localized && { localized: true },
3371
+ ...redirectedTo && { redirectedTo }
3372
+ };
3373
+ }
3374
+ redirection(ctx) {
3375
+ let mode = "default";
3376
+ if (ctx.Composition) mode = "compositionChild";
3377
+ else if (ctx.Parent) mode = "parent";
3378
+ const target = this.visit(asNodes(ctx.qualifiedName)[0]);
3379
+ return {
3380
+ mode,
3381
+ target
3382
+ };
3383
+ }
3384
+ redefinedAssociation(ctx) {
3385
+ const associationKind = asTokens(ctx.Association).length ? "association" : "composition";
3386
+ const name = this.visit(asNodes(ctx.qualifiedName)[0]);
3387
+ const aliasNode = asNodes(ctx.cdsName)[0];
3388
+ const alias = aliasNode ? this.visit(aliasNode) : void 0;
3389
+ const filter = ctx.bracketTokens ? toExpression(asNodes(ctx.bracketTokens)[0]) : void 0;
3390
+ const redirectedTo = ctx.redirection ? this.visit(asNodes(ctx.redirection)[0]) : void 0;
3391
+ return {
3392
+ kind: "redefinedAssociation",
3393
+ associationKind,
3394
+ name,
3395
+ ...filter && { filter },
3396
+ ...alias && { alias },
3397
+ ...redirectedTo && { redirectedTo }
3398
+ };
3399
+ }
3400
+ bindAspect(ctx) {
3401
+ const aspect = this.visit(asNodes(ctx.qualifiedName)[0]);
3402
+ const mappings = asNodes(ctx.bindMapping).map((n) => this.visit(n));
3403
+ const aliasNode = asNodes(ctx.cdsName)[0];
3404
+ const alias = aliasNode ? this.visit(aliasNode) : void 0;
3405
+ return {
3406
+ kind: "aspectBinding",
3407
+ aspect,
3408
+ mappings,
1068
3409
  ...alias && { alias }
1069
3410
  };
1070
3411
  }
3412
+ bindMapping(ctx) {
3413
+ return {
3414
+ element: this.visit(asNodes(ctx.cdsName)[0]),
3415
+ target: formatTokens(collectTokenImages(asNodes(ctx.operand)[0]))
3416
+ };
3417
+ }
1071
3418
  associationTail(ctx) {
1072
3419
  const associationKind = asTokens(ctx.Association).length ? "association" : "composition";
1073
- const cardinality = ctx.cardinality ? this.visit(asNodes(ctx.cardinality)[0]) : void 0;
1074
- let targetMultiplicity;
1075
- if (asTokens(ctx.Many).length) targetMultiplicity = "many";
1076
- else if (asTokens(ctx.One).length) targetMultiplicity = "one";
3420
+ const cardinality = this.optional(ctx, "cardinality");
3421
+ const { wordCardinality, targetMultiplicity } = this.associationWords(ctx);
3422
+ const { on, withDefaultFilter } = this.associationConditions(ctx);
3423
+ const toParent = !!asTokens(ctx.Parent).length;
1077
3424
  const redirected = !!asTokens(ctx.Redirected).length;
1078
3425
  const target = this.visit(asNodes(ctx.qualifiedName)[0]);
1079
3426
  const aliasNode = asNodes(ctx.cdsName)[0];
1080
3427
  const alias = aliasNode ? this.visit(aliasNode) : void 0;
1081
- const on = ctx.expression ? toExpression(asNodes(ctx.expression)[0]) : void 0;
1082
3428
  return {
1083
3429
  kind: "association",
1084
3430
  associationKind,
1085
3431
  annotations: [],
1086
3432
  ...cardinality && { cardinality },
3433
+ ...wordCardinality && { wordCardinality },
1087
3434
  ...targetMultiplicity && { targetMultiplicity },
3435
+ ...toParent && { toParent: true },
1088
3436
  target,
1089
3437
  ...redirected && { redirected: true },
1090
3438
  ...alias && { alias },
1091
- ...on && { on }
3439
+ ...on && { on },
3440
+ ...withDefaultFilter && { withDefaultFilter }
3441
+ };
3442
+ }
3443
+ /**
3444
+ * Word-form cardinality — at most one `cardinalWord` per branch:
3445
+ * `of <w> X`, `of <w> to X`, `to <w> X` all give a single target word.
3446
+ */
3447
+ associationWords(ctx) {
3448
+ const words = asNodes(ctx.cardinalWord).map((n) => this.visit(n));
3449
+ if (words.length !== 1) return {};
3450
+ const word = words[0];
3451
+ return {
3452
+ wordCardinality: { target: word },
3453
+ ...(word === "many" || word === "one") && { targetMultiplicity: word }
3454
+ };
3455
+ }
3456
+ /**
3457
+ * `on` and `with default filter` are both optional and share
3458
+ * `ctx.condition` — select by their keyword token, not by index.
3459
+ */
3460
+ associationConditions(ctx) {
3461
+ const conditions = asNodes(ctx.condition);
3462
+ const hasOn = asTokens(ctx.On).length > 0;
3463
+ const on = hasOn ? toExpression(conditions[0]) : void 0;
3464
+ let withDefaultFilter;
3465
+ if (asTokens(ctx.Filter).length > 0) withDefaultFilter = toExpression(conditions[hasOn ? 1 : 0]);
3466
+ return {
3467
+ on,
3468
+ withDefaultFilter
1092
3469
  };
1093
3470
  }
1094
3471
  cardinality(ctx) {
@@ -1176,26 +3553,319 @@ var CdsVisitor = class extends BaseCstVisitor {
1176
3553
  ...alias && { alias }
1177
3554
  };
1178
3555
  }
3556
+ scalarFunctionDefinition(ctx) {
3557
+ return {
3558
+ kind: "scalarFunction",
3559
+ name: this.visit(asNodes(ctx.cdsName)[0]),
3560
+ annotations: [],
3561
+ parameters: ctx.parametersClause ? this.visit(asNodes(ctx.parametersClause)[0]) : [],
3562
+ returnType: this.visit(asNodes(ctx.typeReference)[0])
3563
+ };
3564
+ }
3565
+ hierarchyDefinition(ctx) {
3566
+ const name = this.visit(asNodes(ctx.cdsName)[0]);
3567
+ const parameters = ctx.parametersClause ? this.visit(asNodes(ctx.parametersClause)[0]) : [];
3568
+ const source = this.visit(asNodes(ctx.qualifiedName)[0]) ?? "";
3569
+ const childToParentAssociation = asNodes(ctx.cdsName)[1] ? this.visit(asNodes(ctx.cdsName)[1]) : void 0;
3570
+ const period = this.optional(ctx, "hierarchyPeriod");
3571
+ const directory = this.optional(ctx, "hierarchyDirectory");
3572
+ const startWhere = this.optional(ctx, "hierarchyStart");
3573
+ const siblingsOrderBy = asNodes(ctx.sortItem).map((n) => this.visit(n));
3574
+ const depth = this.optional(ctx, "hierarchyDepth");
3575
+ const nodetype = this.optional(ctx, "hierarchyNodetype");
3576
+ const load = this.optional(ctx, "hierarchyLoad");
3577
+ const multipleParents = this.optional(ctx, "hierarchyMultiple");
3578
+ const orphans = this.optional(ctx, "hierarchyOrphans");
3579
+ const cycles = this.optional(ctx, "hierarchyCycles");
3580
+ const cache = this.optional(ctx, "hierarchyCache");
3581
+ return {
3582
+ kind: "hierarchy",
3583
+ name,
3584
+ annotations: [],
3585
+ parameters,
3586
+ source,
3587
+ siblingsOrderBy,
3588
+ members: ctx.elementList ? this.visit(asNodes(ctx.elementList)[0]) : [],
3589
+ ...childToParentAssociation && { childToParentAssociation },
3590
+ ...this.hierarchyOptionals(ctx, {
3591
+ period,
3592
+ directory,
3593
+ startWhere,
3594
+ depth,
3595
+ nodetype,
3596
+ load,
3597
+ multipleParents,
3598
+ orphans,
3599
+ cycles,
3600
+ cache
3601
+ })
3602
+ };
3603
+ }
3604
+ /** The clause-dependent optional fields of a hierarchy definition. */
3605
+ hierarchyOptionals(ctx, c) {
3606
+ return {
3607
+ ...c.period && { period: c.period },
3608
+ ...c.directory && { directory: c.directory },
3609
+ ...c.startWhere && { startWhere: c.startWhere },
3610
+ ...c.depth !== void 0 && { depth: c.depth },
3611
+ ...c.nodetype && { nodetype: c.nodetype },
3612
+ ...c.load && { load: c.load },
3613
+ ...c.multipleParents && { multipleParents: c.multipleParents },
3614
+ ...c.orphans && { orphans: c.orphans },
3615
+ ...c.cycles && { cycles: c.cycles },
3616
+ ...ctx.hierarchyGenerate && { generateSpantree: true },
3617
+ ...c.cache && { cache: c.cache }
3618
+ };
3619
+ }
3620
+ hierarchyPeriod(ctx) {
3621
+ const names = asNodes(ctx.qualifiedName).map((n) => this.visit(n));
3622
+ const period = {
3623
+ from: names[0] ?? "",
3624
+ to: names[1] ?? ""
3625
+ };
3626
+ if (ctx.Valid) {
3627
+ period.validFrom = names[2] ?? "";
3628
+ period.validTo = names[3] ?? "";
3629
+ }
3630
+ return period;
3631
+ }
3632
+ hierarchyDirectory(ctx) {
3633
+ return {
3634
+ entity: this.visit(asNodes(ctx.qualifiedName)[0]),
3635
+ filter: toExpression(asNodes(ctx.condition)[0])
3636
+ };
3637
+ }
3638
+ hierarchyStart(ctx) {
3639
+ return toExpression(asNodes(ctx.condition)[0]);
3640
+ }
3641
+ hierarchyDepth(ctx) {
3642
+ const num = asTokens(ctx.NumberLiteral)[0];
3643
+ if (num) return num.image;
3644
+ return this.visit(asNodes(ctx.qualifiedName)[0]);
3645
+ }
3646
+ hierarchyNodetype(ctx) {
3647
+ return this.visit(asNodes(ctx.qualifiedName)[0]);
3648
+ }
3649
+ hierarchyLoad(ctx) {
3650
+ if (asTokens(ctx.Bulk).length) return "bulk";
3651
+ if (asTokens(ctx.Incremental).length) return "incremental";
3652
+ if (ctx.systemVariable) return formatTokens(collectTokenImages(asNodes(ctx.systemVariable)[0]));
3653
+ return this.visit(asNodes(ctx.qualifiedName)[0]);
3654
+ }
3655
+ hierarchyMultiple(ctx) {
3656
+ if (asTokens(ctx.Not).length) return "notAllowed";
3657
+ if (asTokens(ctx.Leaves).length) return "leavesOnly";
3658
+ return "allowed";
3659
+ }
3660
+ hierarchyOrphans(ctx) {
3661
+ if (asTokens(ctx.Ignore).length) return "ignore";
3662
+ if (asTokens(ctx.Error).length) return "error";
3663
+ return "root";
3664
+ }
3665
+ hierarchyCycles(ctx) {
3666
+ return asTokens(ctx.Breakup).length ? "breakup" : "error";
3667
+ }
3668
+ hierarchyGenerate() {
3669
+ return null;
3670
+ }
3671
+ hierarchyCache(ctx) {
3672
+ if (asTokens(ctx.Off).length) return "off";
3673
+ if (asTokens(ctx.Force).length) return "force";
3674
+ return "on";
3675
+ }
3676
+ sortItem(ctx) {
3677
+ const name = this.visit(asNodes(ctx.qualifiedName)[0]);
3678
+ let direction;
3679
+ if (asTokens(ctx.Descending).length) direction = "descending";
3680
+ else if (asTokens(ctx.Ascending).length) direction = "ascending";
3681
+ return {
3682
+ name,
3683
+ ...direction && { direction }
3684
+ };
3685
+ }
3686
+ aspectDefinition(ctx) {
3687
+ return {
3688
+ kind: "aspect",
3689
+ name: this.visit(asNodes(ctx.cdsName)[0]),
3690
+ annotations: [],
3691
+ members: asNodes(ctx.aspectElement).map((n) => this.visit(n))
3692
+ };
3693
+ }
3694
+ aspectElement(ctx) {
3695
+ const annotations = asNodes(ctx.annotation).map((n) => this.visit(n));
3696
+ const isKey = !!asTokens(ctx.Key).length;
3697
+ const name = this.visit(asNodes(ctx.cdsName)[0]);
3698
+ if (ctx.typeReference) return {
3699
+ annotations,
3700
+ name,
3701
+ isKey,
3702
+ type: this.visit(asNodes(ctx.typeReference)[0])
3703
+ };
3704
+ return {
3705
+ annotations,
3706
+ name,
3707
+ isKey,
3708
+ expression: formatTokens(collectTokenImages(asNodes(ctx.operand)[0]))
3709
+ };
3710
+ }
3711
+ annotationDefinition(ctx) {
3712
+ return {
3713
+ kind: "annotationDefinition",
3714
+ name: this.visit(asNodes(ctx.cdsName)[0]),
3715
+ annotations: [],
3716
+ body: formatTokens(collectTokenImages(asNodes(ctx.tokenBlock)[0] ?? asNodes(ctx.simpleTail)[0]))
3717
+ };
3718
+ }
3719
+ accessPolicyDefinition(ctx) {
3720
+ return {
3721
+ kind: "accessPolicy",
3722
+ name: this.visit(asNodes(ctx.cdsName)[0]),
3723
+ annotations: [],
3724
+ body: formatTokens(collectTokenImages(asNodes(ctx.tokenBlock)[0]))
3725
+ };
3726
+ }
3727
+ tokenBlock() {
3728
+ return null;
3729
+ }
3730
+ tokenGroup() {
3731
+ return null;
3732
+ }
3733
+ simpleTail() {
3734
+ return null;
3735
+ }
1179
3736
  roleDefinition(ctx) {
3737
+ const name = this.visit(asNodes(ctx.cdsName)[0]);
3738
+ const switchable = !!asTokens(ctx.Switchable).length;
3739
+ const members = asNodes(ctx.roleMember).map((n) => this.visit(n));
3740
+ const grants = members.filter((m) => "privilege" in m);
1180
3741
  return {
1181
3742
  kind: "role",
1182
- name: this.visit(asNodes(ctx.cdsName)[0]),
3743
+ name,
1183
3744
  annotations: [],
1184
- grants: asNodes(ctx.grantStatement).map((n) => this.visit(n))
3745
+ ...switchable && { switchable: true },
3746
+ grants,
3747
+ members
1185
3748
  };
1186
3749
  }
3750
+ roleMember(ctx) {
3751
+ if (ctx.inheritStatement) return this.visit(asNodes(ctx.inheritStatement)[0]);
3752
+ if (ctx.inheritingStatement) return this.visit(asNodes(ctx.inheritingStatement)[0]);
3753
+ return this.visit(asNodes(ctx.grantStatement)[0]);
3754
+ }
1187
3755
  grantStatement(ctx) {
1188
- const entity = this.visit(asNodes(ctx.qualifiedName)[0]);
1189
- const where = ctx.expression ? toExpression(asNodes(ctx.expression)[0]) : void 0;
1190
- return {
3756
+ const result = {
1191
3757
  privilege: "select",
1192
- entity,
3758
+ entity: this.visit(asNodes(ctx.qualifiedName)[0])
3759
+ };
3760
+ for (const clauseNode of asNodes(ctx.grantClause)) {
3761
+ const clause = this.visit(clauseNode);
3762
+ if (clause.optionalElements) result.optionalElements = clause.optionalElements;
3763
+ if (clause.combinationMode) result.combinationMode = clause.combinationMode;
3764
+ if (clause.inheritRole) result.inheritRole = clause.inheritRole;
3765
+ if (clause.inheritConditionConnector) {
3766
+ result.inheritConditionConnector = clause.inheritConditionConnector;
3767
+ result.inheritCondition = clause.inheritCondition;
3768
+ }
3769
+ if (clause.where) result.where = clause.where;
3770
+ }
3771
+ return result;
3772
+ }
3773
+ grantClause(ctx) {
3774
+ for (const key of Object.keys(ctx)) {
3775
+ const node = asNodes(ctx[key])[0];
3776
+ if (node) return this.visit(node);
3777
+ }
3778
+ return {};
3779
+ }
3780
+ grantOptionalElements(ctx) {
3781
+ return { optionalElements: asNodes(ctx.optionalElement).map((n) => this.visit(n)) };
3782
+ }
3783
+ grantCombination(ctx) {
3784
+ let combinationMode = "and";
3785
+ if (ctx.Redefinition) combinationMode = "redefinition";
3786
+ else if (ctx.Or) combinationMode = "or";
3787
+ return { combinationMode };
3788
+ }
3789
+ grantInherit(ctx) {
3790
+ const inheritRole = this.visit(asNodes(ctx.qualifiedName)[0]);
3791
+ let inheritConditionConnector;
3792
+ if (ctx.dclCondition) inheritConditionConnector = asTokens(ctx.And).length ? "and" : "or";
3793
+ return {
3794
+ inheritRole,
3795
+ ...inheritConditionConnector && { inheritConditionConnector },
3796
+ ...ctx.dclCondition && { inheritCondition: toExpression(asNodes(ctx.dclCondition)[0]) }
3797
+ };
3798
+ }
3799
+ grantWhere(ctx) {
3800
+ return { where: toExpression(asNodes(ctx.dclCondition)[0]) };
3801
+ }
3802
+ optionalElement(ctx) {
3803
+ const name = this.visit(asNodes(ctx.qualifiedName)[0]);
3804
+ const def = ctx.Default ? asTokens(ctx.True).length > 0 : void 0;
3805
+ return {
3806
+ name,
3807
+ ...def !== void 0 && { default: def }
3808
+ };
3809
+ }
3810
+ inheritStatement(ctx) {
3811
+ const names = asNodes(ctx.qualifiedName).map((n) => this.visit(n));
3812
+ const where = ctx.dclCondition ? toExpression(asNodes(ctx.dclCondition)[0]) : void 0;
3813
+ return {
3814
+ role: names[0] ?? "",
3815
+ ...names.length > 1 && { entity: names[1] },
1193
3816
  ...where && { where }
1194
3817
  };
1195
3818
  }
3819
+ inheritingStatement(ctx) {
3820
+ const sourceKind = ctx.Entity ? "entity" : "super";
3821
+ const entity = ctx.Entity ? this.visit(asNodes(ctx.qualifiedName)[0]) : void 0;
3822
+ const def = ctx.Default ? asTokens(ctx.True).length > 0 : void 0;
3823
+ const condition = ctx.dclCondition ? toExpression(asNodes(ctx.dclCondition)[0]) : void 0;
3824
+ return {
3825
+ sourceKind,
3826
+ ...entity && { entity },
3827
+ ...def !== void 0 && { default: def },
3828
+ ...condition && { condition }
3829
+ };
3830
+ }
3831
+ dclCondition() {
3832
+ return null;
3833
+ }
3834
+ extendStatement(ctx) {
3835
+ const names = asNodes(ctx.qualifiedName).map((n) => this.visit(n));
3836
+ let targetKind = "custom";
3837
+ if (ctx.View) targetKind = "view";
3838
+ else if (ctx.Abstract) targetKind = "abstract";
3839
+ const isViewEntity = !!asTokens(ctx.Entity).length;
3840
+ const name = names[0] ?? "";
3841
+ const associations = asNodes(ctx.associationTail).map((node) => this.visit(node));
3842
+ const members = ctx.elementList ? this.visit(asNodes(ctx.elementList)[0]) : [];
3843
+ const groupBy = ctx.Group ? names.slice(1) : void 0;
3844
+ const setOperations = asNodes(ctx.extendSetOperation).map((n) => this.visit(n));
3845
+ return {
3846
+ kind: "extension",
3847
+ name,
3848
+ annotations: [],
3849
+ targetKind,
3850
+ ...isViewEntity && { isViewEntity: true },
3851
+ associations,
3852
+ members,
3853
+ ...groupBy && groupBy.length > 0 && { groupBy },
3854
+ ...setOperations.length > 0 && { setOperations }
3855
+ };
3856
+ }
3857
+ extendSetOperation(ctx) {
3858
+ const members = ctx.elementList ? this.visit(asNodes(ctx.elementList)[0]) : [];
3859
+ return {
3860
+ kind: "extendSetOperation",
3861
+ operator: this.setOperator(ctx),
3862
+ members
3863
+ };
3864
+ }
1196
3865
  annotateStatement(ctx) {
1197
3866
  return {
1198
3867
  kind: "metadataExtension",
3868
+ targetKind: ctx.View ? "view" : "entity",
1199
3869
  entity: this.visit(asNodes(ctx.cdsName)[0]),
1200
3870
  annotations: [],
1201
3871
  elements: asNodes(ctx.annotatedElement).map((node) => this.visit(node))
@@ -1204,7 +3874,7 @@ var CdsVisitor = class extends BaseCstVisitor {
1204
3874
  annotatedElement(ctx) {
1205
3875
  return {
1206
3876
  annotations: asNodes(ctx.annotation).map((node) => this.visit(node)),
1207
- name: this.visit(asNodes(ctx.cdsName)[0])
3877
+ name: this.visit(asNodes(ctx.qualifiedName)[0])
1208
3878
  };
1209
3879
  }
1210
3880
  topLevelAnnotation(ctx) {
@@ -1265,10 +3935,18 @@ var CdsVisitor = class extends BaseCstVisitor {
1265
3935
  };
1266
3936
  }
1267
3937
  literal(ctx) {
1268
- if (ctx.StringLiteral) return {
1269
- kind: "string",
1270
- value: asTokens(ctx.StringLiteral)[0].image.slice(1, -1)
1271
- };
3938
+ const negative = !!asTokens(ctx.Minus).length;
3939
+ if (ctx.StringLiteral) {
3940
+ const raw = asTokens(ctx.StringLiteral)[0].image;
3941
+ if (ctx.Abap) return {
3942
+ kind: "string",
3943
+ value: formatTokens(ctxTokens(ctx))
3944
+ };
3945
+ return {
3946
+ kind: "string",
3947
+ value: raw.slice(1, -1)
3948
+ };
3949
+ }
1272
3950
  if (ctx.EnumLiteral) return {
1273
3951
  kind: "enum",
1274
3952
  value: asTokens(ctx.EnumLiteral)[0].image.slice(1)
@@ -1281,11 +3959,25 @@ var CdsVisitor = class extends BaseCstVisitor {
1281
3959
  kind: "boolean",
1282
3960
  value: false
1283
3961
  };
3962
+ if (ctx.Null) return {
3963
+ kind: "string",
3964
+ value: "null"
3965
+ };
3966
+ const num = Number.parseFloat(asTokens(ctx.NumberLiteral)[0].image);
1284
3967
  return {
1285
3968
  kind: "number",
1286
- value: Number.parseFloat(asTokens(ctx.NumberLiteral)[0].image)
3969
+ value: negative ? -num : num
1287
3970
  };
1288
3971
  }
3972
+ condition() {
3973
+ return null;
3974
+ }
3975
+ conditionOperand() {
3976
+ return null;
3977
+ }
3978
+ predicate() {
3979
+ return null;
3980
+ }
1289
3981
  expression() {
1290
3982
  return null;
1291
3983
  }
@@ -1295,6 +3987,39 @@ var CdsVisitor = class extends BaseCstVisitor {
1295
3987
  operand() {
1296
3988
  return null;
1297
3989
  }
3990
+ primary() {
3991
+ return null;
3992
+ }
3993
+ pathOperand() {
3994
+ return null;
3995
+ }
3996
+ pathSegment() {
3997
+ return null;
3998
+ }
3999
+ bracketTokens() {
4000
+ return null;
4001
+ }
4002
+ callArgument() {
4003
+ return null;
4004
+ }
4005
+ overClause() {
4006
+ return null;
4007
+ }
4008
+ parenthesizedExpression() {
4009
+ return null;
4010
+ }
4011
+ castExpression() {
4012
+ return null;
4013
+ }
4014
+ caseExpression() {
4015
+ return null;
4016
+ }
4017
+ systemVariable() {
4018
+ return null;
4019
+ }
4020
+ parameterRef() {
4021
+ return null;
4022
+ }
1298
4023
  typeReference(ctx) {
1299
4024
  if (ctx.builtinType) return this.visit(asNodes(ctx.builtinType)[0]);
1300
4025
  return this.visit(asNodes(ctx.namedType)[0]);
@@ -1323,6 +4048,9 @@ var CdsVisitor = class extends BaseCstVisitor {
1323
4048
  }
1324
4049
  return "";
1325
4050
  }
4051
+ bareAliasName(ctx) {
4052
+ return this.cdsName(ctx);
4053
+ }
1326
4054
  qualifiedName(ctx) {
1327
4055
  return asNodes(ctx.cdsName).map((node) => this.visit(node)).join(".");
1328
4056
  }
@@ -1356,14 +4084,14 @@ function fromParseError(err) {
1356
4084
  //#region src/lib/ast/walker.ts
1357
4085
  /** True when the definition has a `members` array (view-like entities). */
1358
4086
  function hasMembers(def) {
1359
- return def.kind === "viewEntity" || def.kind === "abstractEntity" || def.kind === "customEntity";
4087
+ return def.kind === "viewEntity" || def.kind === "abstractEntity" || def.kind === "customEntity" || def.kind === "hierarchy" || def.kind === "extension";
1360
4088
  }
1361
4089
  /** True when the definition has a `members` array of typed fields (TABL/structure). */
1362
4090
  function hasFields(def) {
1363
- return def.kind === "table" || def.kind === "structure";
4091
+ return def.kind === "table" || def.kind === "structure" || def.kind === "externalEntity" || def.kind === "tableFunction";
1364
4092
  }
1365
4093
  function hasParameters(def) {
1366
- return def.kind === "viewEntity" || def.kind === "abstractEntity" || def.kind === "customEntity";
4094
+ return def.kind === "viewEntity" || def.kind === "abstractEntity" || def.kind === "customEntity" || def.kind === "externalEntity" || def.kind === "tableFunction" || def.kind === "scalarFunction" || def.kind === "hierarchy";
1367
4095
  }
1368
4096
  /** Yield every definition in the file (currently one, future-proofed). */
1369
4097
  function* walkDefinitions(file) {
@@ -1376,11 +4104,13 @@ function* walkDefinitions(file) {
1376
4104
  function* walkAnnotations(file) {
1377
4105
  for (const def of walkDefinitions(file)) {
1378
4106
  for (const a of getAnnotations(def)) yield a;
1379
- if (hasFields(def)) {
1380
- for (const m of def.members) if ("annotations" in m) for (const a of m.annotations) yield a;
1381
- }
1382
- if (hasMembers(def)) for (const m of def.members) for (const a of m.annotations) yield a;
1383
- if (hasParameters(def)) for (const p of def.parameters) for (const a of p.annotations) yield a;
4107
+ const pools = [];
4108
+ if (hasFields(def)) pools.push(def.members);
4109
+ if (hasMembers(def)) pools.push(def.members);
4110
+ if (def.kind === "aspect") pools.push(def.members);
4111
+ if (hasParameters(def)) pools.push(def.parameters);
4112
+ if (def.kind === "extension") pools.push(def.associations);
4113
+ for (const pool of pools) for (const m of pool) if (typeof m === "object" && m !== null && "annotations" in m) for (const a of m.annotations) yield a;
1384
4114
  }
1385
4115
  }
1386
4116
  /** Safe accessor — returns an empty array if the definition carries none. */
@@ -1404,15 +4134,62 @@ function* walkViewElements(file) {
1404
4134
  function* walkAssociations(file) {
1405
4135
  for (const def of walkDefinitions(file)) {
1406
4136
  if (!hasMembers(def)) continue;
1407
- for (const m of def.members) {
1408
- if (!isAssociation(m)) continue;
1409
- yield {
1410
- owner: def,
1411
- association: m
1412
- };
1413
- }
4137
+ const pools = [def.members.filter(isAssociation)];
4138
+ if (def.kind === "extension") pools.push(def.associations);
4139
+ for (const association of pools.flat()) yield {
4140
+ owner: def,
4141
+ association
4142
+ };
4143
+ }
4144
+ }
4145
+ /**
4146
+ * Yield every association declared in the FROM region of a view entity.
4147
+ *
4148
+ * FROM associations are declared next to the data source (before the element
4149
+ * list) and are therefore kept apart from the associations inside
4150
+ * `members`.
4151
+ */
4152
+ function* walkFromAssociations(file) {
4153
+ for (const def of walkDefinitions(file)) {
4154
+ if (def.kind !== "viewEntity") continue;
4155
+ yield* fromAssocOfSelect(def, def);
4156
+ for (const op of def.setOperations ?? []) yield* fromAssocOfSelect(def, op.statement);
4157
+ }
4158
+ }
4159
+ function* fromAssocOfSelect(owner, select) {
4160
+ for (const a of select.fromAssociations ?? []) yield {
4161
+ owner,
4162
+ association: a
4163
+ };
4164
+ for (const op of select.setOperations ?? []) yield* fromAssocOfSelect(owner, op.statement);
4165
+ }
4166
+ /** Yield every join of every view entity, in source order. */
4167
+ function* walkJoins(file) {
4168
+ for (const def of walkDefinitions(file)) {
4169
+ if (def.kind !== "viewEntity") continue;
4170
+ yield* walkJoinChain(def, def.joins ?? []);
4171
+ for (const op of def.setOperations ?? []) yield* walkSelectJoins(def, op.statement);
4172
+ }
4173
+ }
4174
+ function* walkSelectJoins(owner, select) {
4175
+ yield* walkJoinChain(owner, select.joins ?? []);
4176
+ for (const op of select.setOperations ?? []) yield* walkSelectJoins(owner, op.statement);
4177
+ }
4178
+ function* walkJoinChain(owner, joins) {
4179
+ for (const join of joins) {
4180
+ yield {
4181
+ owner,
4182
+ join
4183
+ };
4184
+ yield* walkJoinSource(owner, join.source);
1414
4185
  }
1415
4186
  }
4187
+ /** Descend into a grouped join source (`join (A join B on …) on …`). */
4188
+ function* walkJoinSource(owner, source) {
4189
+ if (source.kind !== "joinGroup") return;
4190
+ yield* walkJoinSource(owner, source.source);
4191
+ yield* walkJoinChain(owner, source.joins);
4192
+ }
1416
4193
  /** Yield every field definition from tables and structures. */
1417
4194
  function* walkFields(file) {
1418
4195
  for (const def of walkDefinitions(file)) {
@@ -1448,7 +4225,8 @@ function findAnnotation(annotations, key) {
1448
4225
  //#region src/lib/validate/cardinality.ts
1449
4226
  function validateCardinality(file) {
1450
4227
  const diagnostics = [];
1451
- for (const { association } of walkAssociations(file)) {
4228
+ const associations = [...walkAssociations(file), ...walkFromAssociations(file)];
4229
+ for (const { association } of associations) {
1452
4230
  const card = association.cardinality;
1453
4231
  if (!card) continue;
1454
4232
  const { min, max } = card;
@@ -1513,13 +4291,32 @@ const ddlCoverage = {
1513
4291
  "define structure",
1514
4292
  "define type",
1515
4293
  "define view entity",
4294
+ "define root view entity",
4295
+ "define view (legacy classic SQL view)",
4296
+ "define view entity as projection on (projection view)",
4297
+ "provider contract transactional_query",
1516
4298
  "define abstract entity",
1517
4299
  "define custom entity",
4300
+ "define table entity",
4301
+ "define external entity",
4302
+ "define writable external entity",
4303
+ "define table function",
4304
+ "define scalar function",
4305
+ "define hierarchy",
4306
+ "define aspect",
4307
+ "define annotation",
4308
+ "define accesspolicy",
1518
4309
  "define service",
1519
4310
  "annotate entity",
4311
+ "extend view entity / extend view / extend abstract|custom entity",
4312
+ "select distinct",
4313
+ "where / group by / having",
4314
+ "union [all] / intersect / except",
4315
+ "bind aspect <aspect>(<bindings>)",
1520
4316
  "include directive",
1521
4317
  "builtin type (abap.char, abap.dec, ...)",
1522
- "named type reference"
4318
+ "named type reference",
4319
+ "typed literal (abap.dats'20200101')"
1523
4320
  ]
1524
4321
  };
1525
4322
 
@@ -1529,8 +4326,13 @@ const dclCoverage = {
1529
4326
  topic: "dcl",
1530
4327
  constructs: [
1531
4328
  "define role <name> { ... }",
4329
+ "define role <name> switchable { ... }",
1532
4330
  "grant select on <entity>",
1533
- "grant select on <entity> where <expression>"
4331
+ "grant select on <entity> where <expression>",
4332
+ "grant select on <entity> with optional elements ( ... )",
4333
+ "grant ... combination mode and|or|redefinition",
4334
+ "inherit <role> [for grant select on <entity>] [where ...]",
4335
+ "inheriting conditions from entity <name>|super [default ...]"
1534
4336
  ]
1535
4337
  };
1536
4338
 
@@ -1554,13 +4356,49 @@ const associationsCoverage = {
1554
4356
  topic: "associations",
1555
4357
  constructs: [
1556
4358
  "association to <target>",
4359
+ "association <target> (implicit to)",
1557
4360
  "association[cardinality] to <target>",
1558
4361
  "association[L..U] to <target>",
1559
- "association of (many|one) to <target>",
1560
- "composition to <target>",
4362
+ "association of (many|one|exact one) [to] <target>",
4363
+ "composition [of] <target>",
4364
+ "association to parent <target>",
1561
4365
  "association to redirected to <target>",
1562
4366
  "association ... as <alias>",
1563
- "association ... on <expression>"
4367
+ "association ... on <expression>",
4368
+ "association ... with default filter <expression>",
4369
+ "FROM-region association / composition",
4370
+ "bind aspect <name>(<bindings>)"
4371
+ ]
4372
+ };
4373
+
4374
+ //#endregion
4375
+ //#region src/lib/grammar/joins.ts
4376
+ const joinsCoverage = {
4377
+ topic: "joins",
4378
+ constructs: [
4379
+ "JOIN <source> ON <condition> (inner)",
4380
+ "INNER JOIN <source> ON <condition> (inner)",
4381
+ "LEFT JOIN <source> ON <condition> (leftOuter)",
4382
+ "LEFT OUTER JOIN <source> ON <condition> (leftOuter)",
4383
+ "RIGHT JOIN <source> ON <condition> (rightOuter)",
4384
+ "RIGHT OUTER JOIN <source> ON <condition> (rightOuter)",
4385
+ "FULL JOIN <source> ON <condition> (fullOuter)",
4386
+ "FULL OUTER JOIN <source> ON <condition> (fullOuter)",
4387
+ "CROSS JOIN <source> (cross, no ON condition)",
4388
+ "chained joins (multiple joins in one FROM clause)",
4389
+ "join source alias",
4390
+ "parameterized data source (<param> : <value>)",
4391
+ "parameter binding to a view parameter (:P_Param)",
4392
+ "parameter binding to a system variable ($session.client, $parameters.P_X)",
4393
+ "parameter binding to a literal",
4394
+ "ON condition with AND / OR / NOT predicates",
4395
+ "ON condition with parentheses and nested call arguments",
4396
+ "ON condition with `is null` / `is not initial` predicates",
4397
+ "ON condition with a system variable ($projection.Field)",
4398
+ "ON condition with LIKE / BETWEEN / IN predicates",
4399
+ "ON condition with arithmetic operands (B.k + 1 = A.k)",
4400
+ "join cardinality ([INNER] TO ONE|MANY JOIN, MANY TO ONE JOIN, ...)",
4401
+ "grouped joins ((A join B on ...) left join C on ...)"
1564
4402
  ]
1565
4403
  };
1566
4404
 
@@ -1584,8 +4422,15 @@ const projectionsCoverage = {
1584
4422
  "view element with alias",
1585
4423
  "view element with key modifier",
1586
4424
  "view element with virtual modifier",
4425
+ "view element with function call (coalesce(a, b), sum(x))",
4426
+ "window function over (partition by ... order by ...)",
4427
+ "view element with cast expression (cast(x as type [preserving type]))",
1587
4428
  "typed field (abstract/custom entity)",
1588
- "view where clause",
4429
+ "case expression (operand and searched forms)",
4430
+ "arithmetic expression in element list",
4431
+ "localized element (: localized)",
4432
+ "association redirection (: redirected to <target>)",
4433
+ "view where / group by / having clauses",
1589
4434
  "projection view (as projection on <source>)"
1590
4435
  ]
1591
4436
  };
@@ -1604,6 +4449,7 @@ const GRAMMAR_COVERAGE = [
1604
4449
  dclCoverage,
1605
4450
  annotationsCoverage,
1606
4451
  associationsCoverage,
4452
+ joinsCoverage,
1607
4453
  parametersCoverage,
1608
4454
  projectionsCoverage,
1609
4455
  actionsCoverage
@@ -1656,5 +4502,5 @@ function parse(source) {
1656
4502
  }
1657
4503
 
1658
4504
  //#endregion
1659
- export { GRAMMAR_COVERAGE, actionsCoverage, annotationsCoverage, associationsCoverage, dclCoverage, ddlCoverage, findAnnotation, hasFields, hasMembers, hasParameters, isAssociation, parametersCoverage, parse, projectionsCoverage, validate, validateCardinality, validateViewElements, walkAnnotations, walkAssociations, walkDefinitions, walkFields, walkParameters, walkViewElements };
4505
+ export { GRAMMAR_COVERAGE, actionsCoverage, annotationsCoverage, associationsCoverage, dclCoverage, ddlCoverage, findAnnotation, hasFields, hasMembers, hasParameters, isAssociation, joinsCoverage, parametersCoverage, parse, projectionsCoverage, validate, validateCardinality, validateViewElements, walkAnnotations, walkAssociations, walkDefinitions, walkFields, walkFromAssociations, walkJoins, walkParameters, walkViewElements };
1660
4506
  //# sourceMappingURL=index.mjs.map