@abapify/acds 0.4.0 → 0.4.1

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 ADDED
@@ -0,0 +1,1660 @@
1
+ import { CstParser, Lexer, createToken } from "chevrotain";
2
+
3
+ //#region src/tokens.ts
4
+ /**
5
+ * Chevrotain Token Definitions for ABAP CDS
6
+ *
7
+ * Token order matters: more specific patterns must come before general ones.
8
+ * Keywords use `longer_alt` to avoid matching prefixes of identifiers.
9
+ */
10
+ const WhiteSpace = createToken({
11
+ name: "WhiteSpace",
12
+ pattern: /\s+/,
13
+ group: Lexer.SKIPPED
14
+ });
15
+ const LineComment = createToken({
16
+ name: "LineComment",
17
+ pattern: /\/\/[^\n]*/,
18
+ group: Lexer.SKIPPED
19
+ });
20
+ const BlockComment = createToken({
21
+ name: "BlockComment",
22
+ pattern: /\/\*[\s\S]*?\*\//,
23
+ group: Lexer.SKIPPED
24
+ });
25
+ const StringLiteral = createToken({
26
+ name: "StringLiteral",
27
+ pattern: /'(?:[^'\\]|\\.)*'/
28
+ });
29
+ const NumberLiteral = createToken({
30
+ name: "NumberLiteral",
31
+ pattern: /\d+(?:\.\d+)?/
32
+ });
33
+ /** Hash-prefixed enum value: #TRANSPARENT, #NOT_EXTENSIBLE */
34
+ const EnumLiteral = createToken({
35
+ name: "EnumLiteral",
36
+ pattern: /#[A-Za-z_][A-Za-z0-9_]*/
37
+ });
38
+ const Identifier = createToken({
39
+ name: "Identifier",
40
+ pattern: /[A-Za-z_][A-Za-z0-9_]*/
41
+ });
42
+ const Define = createToken({
43
+ name: "Define",
44
+ pattern: /define/,
45
+ longer_alt: Identifier
46
+ });
47
+ const Table = createToken({
48
+ name: "Table",
49
+ pattern: /table/,
50
+ longer_alt: Identifier
51
+ });
52
+ const Structure = createToken({
53
+ name: "Structure",
54
+ pattern: /structure/,
55
+ longer_alt: Identifier
56
+ });
57
+ const Type = createToken({
58
+ name: "Type",
59
+ pattern: /type/,
60
+ longer_alt: Identifier
61
+ });
62
+ const Service = createToken({
63
+ name: "Service",
64
+ pattern: /service/,
65
+ longer_alt: Identifier
66
+ });
67
+ const Expose = createToken({
68
+ name: "Expose",
69
+ pattern: /expose/,
70
+ longer_alt: Identifier
71
+ });
72
+ const Annotate = createToken({
73
+ name: "Annotate",
74
+ pattern: /annotate/,
75
+ longer_alt: Identifier
76
+ });
77
+ const Entity = createToken({
78
+ name: "Entity",
79
+ pattern: /entity/,
80
+ longer_alt: Identifier
81
+ });
82
+ const With = createToken({
83
+ name: "With",
84
+ pattern: /with/,
85
+ longer_alt: Identifier
86
+ });
87
+ const Key = createToken({
88
+ name: "Key",
89
+ pattern: /key/,
90
+ longer_alt: Identifier
91
+ });
92
+ const Not = createToken({
93
+ name: "Not",
94
+ pattern: /not/,
95
+ longer_alt: Identifier
96
+ });
97
+ const Null = createToken({
98
+ name: "Null",
99
+ pattern: /null/,
100
+ longer_alt: Identifier
101
+ });
102
+ const As = createToken({
103
+ name: "As",
104
+ pattern: /as/,
105
+ longer_alt: Identifier
106
+ });
107
+ const Include = createToken({
108
+ name: "Include",
109
+ pattern: /include/,
110
+ longer_alt: Identifier
111
+ });
112
+ const Suffix = createToken({
113
+ name: "Suffix",
114
+ pattern: /suffix/,
115
+ longer_alt: Identifier
116
+ });
117
+ const True = createToken({
118
+ name: "True",
119
+ pattern: /true/,
120
+ longer_alt: Identifier
121
+ });
122
+ const False = createToken({
123
+ name: "False",
124
+ pattern: /false/,
125
+ longer_alt: Identifier
126
+ });
127
+ const Abap = createToken({
128
+ name: "Abap",
129
+ pattern: /abap/,
130
+ longer_alt: Identifier
131
+ });
132
+ const View = createToken({
133
+ name: "View",
134
+ pattern: /view/,
135
+ longer_alt: Identifier
136
+ });
137
+ const Projection = createToken({
138
+ name: "Projection",
139
+ pattern: /projection/,
140
+ longer_alt: Identifier
141
+ });
142
+ const Select = createToken({
143
+ name: "Select",
144
+ pattern: /select/,
145
+ longer_alt: Identifier
146
+ });
147
+ const From = createToken({
148
+ name: "From",
149
+ pattern: /from/,
150
+ longer_alt: Identifier
151
+ });
152
+ const On = createToken({
153
+ name: "On",
154
+ pattern: /on/,
155
+ longer_alt: Identifier
156
+ });
157
+ const Association = createToken({
158
+ name: "Association",
159
+ pattern: /association/,
160
+ longer_alt: Identifier
161
+ });
162
+ const Composition = createToken({
163
+ name: "Composition",
164
+ pattern: /composition/,
165
+ longer_alt: Identifier
166
+ });
167
+ const To = createToken({
168
+ name: "To",
169
+ pattern: /to/,
170
+ longer_alt: Identifier
171
+ });
172
+ const Many = createToken({
173
+ name: "Many",
174
+ pattern: /many/,
175
+ longer_alt: Identifier
176
+ });
177
+ const One = createToken({
178
+ name: "One",
179
+ pattern: /one/,
180
+ longer_alt: Identifier
181
+ });
182
+ const Of = createToken({
183
+ name: "Of",
184
+ pattern: /of/,
185
+ longer_alt: Identifier
186
+ });
187
+ const Parameters = createToken({
188
+ name: "Parameters",
189
+ pattern: /parameters/,
190
+ longer_alt: Identifier
191
+ });
192
+ const Role = createToken({
193
+ name: "Role",
194
+ pattern: /role/,
195
+ longer_alt: Identifier
196
+ });
197
+ const Grant = createToken({
198
+ name: "Grant",
199
+ pattern: /grant/,
200
+ longer_alt: Identifier
201
+ });
202
+ const Where = createToken({
203
+ name: "Where",
204
+ pattern: /where/,
205
+ longer_alt: Identifier
206
+ });
207
+ const Abstract = createToken({
208
+ name: "Abstract",
209
+ pattern: /abstract/,
210
+ longer_alt: Identifier
211
+ });
212
+ const Custom = createToken({
213
+ name: "Custom",
214
+ pattern: /custom/,
215
+ longer_alt: Identifier
216
+ });
217
+ const Virtual = createToken({
218
+ name: "Virtual",
219
+ pattern: /virtual/,
220
+ longer_alt: Identifier
221
+ });
222
+ const Redirected = createToken({
223
+ name: "Redirected",
224
+ pattern: /redirected/,
225
+ longer_alt: Identifier
226
+ });
227
+ const Default = createToken({
228
+ name: "Default",
229
+ pattern: /default/,
230
+ longer_alt: Identifier
231
+ });
232
+ const EqEq = createToken({
233
+ name: "EqEq",
234
+ pattern: /==/
235
+ });
236
+ const Eq = createToken({
237
+ name: "Eq",
238
+ pattern: /=/
239
+ });
240
+ const NotEq = createToken({
241
+ name: "NotEq",
242
+ pattern: /!=|<>/
243
+ });
244
+ const LtEq = createToken({
245
+ name: "LtEq",
246
+ pattern: /<=/
247
+ });
248
+ const GtEq = createToken({
249
+ name: "GtEq",
250
+ pattern: />=/
251
+ });
252
+ const Lt = createToken({
253
+ name: "Lt",
254
+ pattern: /</
255
+ });
256
+ const Gt = createToken({
257
+ name: "Gt",
258
+ pattern: />/
259
+ });
260
+ const And = createToken({
261
+ name: "And",
262
+ pattern: /and/,
263
+ longer_alt: Identifier
264
+ });
265
+ const Or = createToken({
266
+ name: "Or",
267
+ pattern: /or/,
268
+ longer_alt: Identifier
269
+ });
270
+ const At = createToken({
271
+ name: "At",
272
+ pattern: /@/
273
+ });
274
+ const Colon = createToken({
275
+ name: "Colon",
276
+ pattern: /:/
277
+ });
278
+ const Semicolon = createToken({
279
+ name: "Semicolon",
280
+ pattern: /;/
281
+ });
282
+ const Dot = createToken({
283
+ name: "Dot",
284
+ pattern: /\./
285
+ });
286
+ const Comma = createToken({
287
+ name: "Comma",
288
+ pattern: /,/
289
+ });
290
+ const LBrace = createToken({
291
+ name: "LBrace",
292
+ pattern: /\{/
293
+ });
294
+ const RBrace = createToken({
295
+ name: "RBrace",
296
+ pattern: /\}/
297
+ });
298
+ const LParen = createToken({
299
+ name: "LParen",
300
+ pattern: /\(/
301
+ });
302
+ const RParen = createToken({
303
+ name: "RParen",
304
+ pattern: /\)/
305
+ });
306
+ const LBracket = createToken({
307
+ name: "LBracket",
308
+ pattern: /\[/
309
+ });
310
+ const RBracket = createToken({
311
+ name: "RBracket",
312
+ pattern: /\]/
313
+ });
314
+ const Star = createToken({
315
+ name: "Star",
316
+ pattern: /\*/
317
+ });
318
+ const Plus = createToken({
319
+ name: "Plus",
320
+ pattern: /\+/
321
+ });
322
+ const Minus = createToken({
323
+ name: "Minus",
324
+ pattern: /-/
325
+ });
326
+ const Slash = createToken({
327
+ name: "Slash",
328
+ pattern: /\//
329
+ });
330
+ const allTokens = [
331
+ WhiteSpace,
332
+ LineComment,
333
+ BlockComment,
334
+ StringLiteral,
335
+ NumberLiteral,
336
+ EnumLiteral,
337
+ Association,
338
+ Composition,
339
+ Abstract,
340
+ Annotate,
341
+ Define,
342
+ Table,
343
+ Structure,
344
+ Type,
345
+ Service,
346
+ Expose,
347
+ Entity,
348
+ With,
349
+ Key,
350
+ Not,
351
+ Null,
352
+ As,
353
+ Include,
354
+ Suffix,
355
+ True,
356
+ False,
357
+ Abap,
358
+ One,
359
+ View,
360
+ Projection,
361
+ Select,
362
+ From,
363
+ On,
364
+ To,
365
+ Many,
366
+ Of,
367
+ Parameters,
368
+ Role,
369
+ Grant,
370
+ Where,
371
+ Custom,
372
+ Virtual,
373
+ Redirected,
374
+ Default,
375
+ And,
376
+ Or,
377
+ Identifier,
378
+ EqEq,
379
+ NotEq,
380
+ LtEq,
381
+ GtEq,
382
+ At,
383
+ Colon,
384
+ Semicolon,
385
+ Dot,
386
+ Comma,
387
+ LBrace,
388
+ RBrace,
389
+ LParen,
390
+ RParen,
391
+ LBracket,
392
+ RBracket,
393
+ Eq,
394
+ Lt,
395
+ Gt,
396
+ Star,
397
+ Plus,
398
+ Minus,
399
+ Slash
400
+ ];
401
+ const CdsLexer = new Lexer(allTokens);
402
+
403
+ //#endregion
404
+ //#region src/parser.ts
405
+ /**
406
+ * Chevrotain CstParser for ABAP CDS.
407
+ *
408
+ * 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 { ... }`)
413
+ * - SRVD : service definitions (`define service ... { expose ...; }`)
414
+ * - Elements / associations / parameters / annotations
415
+ *
416
+ * Rules are kept in this single class (Chevrotain requirement). Logical
417
+ * groupings are delimited with section banners and documented in
418
+ * `src/lib/grammar/`.
419
+ */
420
+ var CdsParser = class extends CstParser {
421
+ constructor() {
422
+ super(allTokens, {
423
+ recoveryEnabled: true,
424
+ maxLookahead: 4
425
+ });
426
+ this.performSelfAnalysis();
427
+ }
428
+ sourceFile = this.RULE("sourceFile", () => {
429
+ this.MANY(() => {
430
+ this.SUBRULE(this.topLevelAnnotation);
431
+ });
432
+ this.SUBRULE(this.definition);
433
+ });
434
+ definition = this.RULE("definition", () => {
435
+ this.OR([{ ALT: () => this.SUBRULE(this.defineStatement) }, { ALT: () => this.SUBRULE(this.annotateStatement) }]);
436
+ });
437
+ defineStatement = this.RULE("defineStatement", () => {
438
+ this.CONSUME(Define);
439
+ this.OR([
440
+ { ALT: () => this.SUBRULE(this.tableDefinition) },
441
+ { ALT: () => this.SUBRULE(this.structureDefinition) },
442
+ { ALT: () => this.SUBRULE(this.viewEntityDefinition) },
443
+ { ALT: () => this.SUBRULE(this.abstractEntityDefinition) },
444
+ { ALT: () => this.SUBRULE(this.customEntityDefinition) },
445
+ { ALT: () => this.SUBRULE(this.simpleTypeDefinition) },
446
+ { ALT: () => this.SUBRULE(this.serviceDefinition) },
447
+ { ALT: () => this.SUBRULE(this.roleDefinition) }
448
+ ]);
449
+ });
450
+ tableDefinition = this.RULE("tableDefinition", () => {
451
+ this.CONSUME(Table);
452
+ this.SUBRULE(this.cdsName);
453
+ this.CONSUME(LBrace);
454
+ this.MANY(() => {
455
+ this.SUBRULE(this.tableMember);
456
+ });
457
+ this.CONSUME(RBrace);
458
+ });
459
+ structureDefinition = this.RULE("structureDefinition", () => {
460
+ this.CONSUME(Structure);
461
+ this.SUBRULE(this.cdsName);
462
+ this.CONSUME(LBrace);
463
+ this.MANY(() => {
464
+ this.SUBRULE(this.tableMember);
465
+ });
466
+ this.CONSUME(RBrace);
467
+ });
468
+ viewEntityDefinition = this.RULE("viewEntityDefinition", () => {
469
+ this.CONSUME(View);
470
+ this.CONSUME(Entity);
471
+ this.SUBRULE(this.cdsName);
472
+ this.OPTION(() => {
473
+ this.SUBRULE(this.parametersClause);
474
+ });
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
+ this.OPTION3(() => {
481
+ this.CONSUME(Where);
482
+ this.SUBRULE(this.expression);
483
+ });
484
+ });
485
+ abstractEntityDefinition = this.RULE("abstractEntityDefinition", () => {
486
+ this.CONSUME(Abstract);
487
+ this.CONSUME(Entity);
488
+ this.SUBRULE(this.cdsName);
489
+ this.OPTION(() => {
490
+ this.SUBRULE(this.parametersClause);
491
+ });
492
+ this.SUBRULE(this.elementList);
493
+ });
494
+ customEntityDefinition = this.RULE("customEntityDefinition", () => {
495
+ this.CONSUME(Custom);
496
+ this.CONSUME(Entity);
497
+ this.SUBRULE(this.cdsName);
498
+ this.OPTION(() => {
499
+ this.SUBRULE(this.parametersClause);
500
+ });
501
+ this.SUBRULE(this.elementList);
502
+ });
503
+ selectFromClause = this.RULE("selectFromClause", () => {
504
+ this.CONSUME(Select);
505
+ this.OPTION(() => {
506
+ this.CONSUME(From);
507
+ });
508
+ this.SUBRULE(this.dataSource);
509
+ });
510
+ projectionOnClause = this.RULE("projectionOnClause", () => {
511
+ this.CONSUME(Projection);
512
+ this.CONSUME(On);
513
+ this.SUBRULE(this.dataSource);
514
+ });
515
+ dataSource = this.RULE("dataSource", () => {
516
+ this.SUBRULE(this.qualifiedName);
517
+ this.OPTION(() => {
518
+ this.OPTION2(() => this.CONSUME(As));
519
+ this.SUBRULE(this.cdsName);
520
+ });
521
+ });
522
+ elementList = this.RULE("elementList", () => {
523
+ this.CONSUME(LBrace);
524
+ this.MANY(() => {
525
+ this.SUBRULE(this.elementMember);
526
+ });
527
+ this.CONSUME(RBrace);
528
+ });
529
+ elementMember = this.RULE("elementMember", () => {
530
+ this.MANY(() => this.SUBRULE(this.annotation));
531
+ this.OR([{ ALT: () => this.SUBRULE(this.associationTail) }, { ALT: () => this.SUBRULE(this.viewElementTail) }]);
532
+ });
533
+ viewElementTail = this.RULE("viewElementTail", () => {
534
+ this.OPTION(() => this.CONSUME(Key));
535
+ this.OPTION2(() => this.CONSUME(Virtual));
536
+ this.OPTION3(() => this.CONSUME(Redirected));
537
+ this.OR([{
538
+ GATE: () => this.isTypedField(),
539
+ ALT: () => this.SUBRULE(this.typedFieldTail)
540
+ }, { ALT: () => this.SUBRULE(this.projectionExpression) }]);
541
+ this.OPTION4(() => {
542
+ this.OR2([{ ALT: () => this.CONSUME(Semicolon) }, { ALT: () => this.CONSUME(Comma) }]);
543
+ });
544
+ });
545
+ typedFieldTail = this.RULE("typedFieldTail", () => {
546
+ this.SUBRULE(this.cdsName);
547
+ this.CONSUME(Colon);
548
+ this.SUBRULE(this.typeReference);
549
+ this.OPTION(() => {
550
+ this.CONSUME(Not);
551
+ this.CONSUME(Null);
552
+ });
553
+ });
554
+ /**
555
+ * projection expression:
556
+ * <qualified-name> [as <alias>]
557
+ * Kept intentionally narrow — full SQL expression support is out of scope.
558
+ */
559
+ projectionExpression = this.RULE("projectionExpression", () => {
560
+ this.SUBRULE(this.qualifiedName);
561
+ this.OPTION(() => {
562
+ this.CONSUME(As);
563
+ this.SUBRULE(this.cdsName);
564
+ });
565
+ });
566
+ /** Cheap lookahead: `ident :` (optionally following `key`/`virtual`) → typed field. */
567
+ isTypedField() {
568
+ const first = this.LA(1);
569
+ const second = this.LA(2);
570
+ if (!first || !second) return false;
571
+ return (first.tokenType?.name === "Identifier" || false) && second.tokenType?.name === "Colon";
572
+ }
573
+ associationTail = this.RULE("associationTail", () => {
574
+ this.OR([{ ALT: () => this.CONSUME(Association) }, { ALT: () => this.CONSUME(Composition) }]);
575
+ 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(() => {
591
+ this.CONSUME(As);
592
+ this.SUBRULE(this.cdsName);
593
+ });
594
+ this.OPTION6(() => {
595
+ this.CONSUME(On);
596
+ this.SUBRULE(this.expression);
597
+ });
598
+ this.OPTION7(() => {
599
+ this.OR4([{ ALT: () => this.CONSUME(Semicolon) }, { ALT: () => this.CONSUME(Comma) }]);
600
+ });
601
+ });
602
+ /** `[<lower>..<upper>]`, `[<n>]`, `[*]`, `[<lower>..*]` */
603
+ cardinality = this.RULE("cardinality", () => {
604
+ this.CONSUME(LBracket);
605
+ this.OR([{ ALT: () => this.CONSUME(Star) }, { ALT: () => {
606
+ this.CONSUME(NumberLiteral);
607
+ this.OPTION(() => {
608
+ this.CONSUME(Dot);
609
+ this.CONSUME2(Dot);
610
+ this.OR2([{ ALT: () => this.CONSUME2(NumberLiteral) }, { ALT: () => this.CONSUME2(Star) }]);
611
+ });
612
+ } }]);
613
+ this.CONSUME(RBracket);
614
+ });
615
+ parametersClause = this.RULE("parametersClause", () => {
616
+ this.CONSUME(With);
617
+ this.CONSUME(Parameters);
618
+ this.SUBRULE(this.parameterDefinition);
619
+ this.MANY(() => {
620
+ this.CONSUME(Comma);
621
+ this.SUBRULE2(this.parameterDefinition);
622
+ });
623
+ });
624
+ parameterDefinition = this.RULE("parameterDefinition", () => {
625
+ this.MANY(() => this.SUBRULE(this.annotation));
626
+ this.SUBRULE(this.cdsName);
627
+ this.CONSUME(Colon);
628
+ this.SUBRULE(this.typeReference);
629
+ this.OPTION(() => {
630
+ this.CONSUME(Default);
631
+ this.SUBRULE(this.literal);
632
+ });
633
+ });
634
+ simpleTypeDefinition = this.RULE("simpleTypeDefinition", () => {
635
+ this.CONSUME(Type);
636
+ this.SUBRULE(this.cdsName);
637
+ this.CONSUME(Colon);
638
+ this.SUBRULE(this.typeReference);
639
+ this.CONSUME(Semicolon);
640
+ });
641
+ serviceDefinition = this.RULE("serviceDefinition", () => {
642
+ this.CONSUME(Service);
643
+ this.SUBRULE(this.cdsName);
644
+ this.CONSUME(LBrace);
645
+ this.MANY(() => {
646
+ this.SUBRULE(this.exposeStatement);
647
+ });
648
+ this.CONSUME(RBrace);
649
+ });
650
+ exposeStatement = this.RULE("exposeStatement", () => {
651
+ this.CONSUME(Expose);
652
+ this.SUBRULE(this.cdsName);
653
+ this.OPTION(() => {
654
+ this.CONSUME(As);
655
+ this.SUBRULE2(this.cdsName);
656
+ });
657
+ this.CONSUME(Semicolon);
658
+ });
659
+ roleDefinition = this.RULE("roleDefinition", () => {
660
+ this.CONSUME(Role);
661
+ this.SUBRULE(this.cdsName);
662
+ this.CONSUME(LBrace);
663
+ this.MANY(() => {
664
+ this.SUBRULE(this.grantStatement);
665
+ });
666
+ this.CONSUME(RBrace);
667
+ });
668
+ grantStatement = this.RULE("grantStatement", () => {
669
+ this.CONSUME(Grant);
670
+ this.CONSUME(Select);
671
+ this.CONSUME(On);
672
+ this.SUBRULE(this.qualifiedName);
673
+ this.OPTION(() => {
674
+ this.CONSUME(Where);
675
+ this.SUBRULE(this.expression);
676
+ });
677
+ this.CONSUME(Semicolon);
678
+ });
679
+ tableMember = this.RULE("tableMember", () => {
680
+ this.OR([{ ALT: () => this.SUBRULE(this.includeDirective) }, { ALT: () => this.SUBRULE(this.fieldDefinition) }]);
681
+ });
682
+ includeDirective = this.RULE("includeDirective", () => {
683
+ this.CONSUME(Include);
684
+ this.SUBRULE(this.cdsName);
685
+ this.OPTION(() => {
686
+ this.CONSUME(With);
687
+ this.CONSUME(Suffix);
688
+ this.SUBRULE2(this.cdsName);
689
+ });
690
+ this.CONSUME(Semicolon);
691
+ });
692
+ fieldDefinition = this.RULE("fieldDefinition", () => {
693
+ this.MANY(() => {
694
+ this.SUBRULE(this.annotation);
695
+ });
696
+ this.OPTION(() => {
697
+ this.CONSUME(Key);
698
+ });
699
+ this.SUBRULE(this.cdsName);
700
+ this.CONSUME(Colon);
701
+ this.SUBRULE(this.typeReference);
702
+ this.OPTION2(() => {
703
+ this.CONSUME(Not);
704
+ this.CONSUME(Null);
705
+ });
706
+ this.CONSUME(Semicolon);
707
+ });
708
+ annotateStatement = this.RULE("annotateStatement", () => {
709
+ this.CONSUME(Annotate);
710
+ this.CONSUME(Entity);
711
+ this.SUBRULE(this.cdsName);
712
+ this.CONSUME(With);
713
+ this.CONSUME(LBrace);
714
+ this.MANY(() => {
715
+ this.SUBRULE(this.annotatedElement);
716
+ });
717
+ this.CONSUME(RBrace);
718
+ });
719
+ annotatedElement = this.RULE("annotatedElement", () => {
720
+ this.MANY(() => {
721
+ this.SUBRULE(this.annotation);
722
+ });
723
+ this.SUBRULE(this.cdsName);
724
+ this.CONSUME(Semicolon);
725
+ });
726
+ topLevelAnnotation = this.RULE("topLevelAnnotation", () => {
727
+ this.SUBRULE(this.annotation);
728
+ });
729
+ annotation = this.RULE("annotation", () => {
730
+ this.CONSUME(At);
731
+ this.SUBRULE(this.dottedName);
732
+ this.OPTION(() => {
733
+ this.CONSUME(Colon);
734
+ this.SUBRULE(this.annotationValue);
735
+ });
736
+ });
737
+ dottedName = this.RULE("dottedName", () => {
738
+ this.SUBRULE(this.cdsName);
739
+ this.MANY(() => {
740
+ this.CONSUME(Dot);
741
+ this.SUBRULE2(this.cdsName);
742
+ });
743
+ });
744
+ annotationValue = this.RULE("annotationValue", () => {
745
+ this.OR([
746
+ { ALT: () => this.CONSUME(StringLiteral) },
747
+ { ALT: () => this.CONSUME(EnumLiteral) },
748
+ { ALT: () => this.CONSUME(True) },
749
+ { ALT: () => this.CONSUME(False) },
750
+ { ALT: () => this.CONSUME(NumberLiteral) },
751
+ { ALT: () => this.SUBRULE(this.annotationArray) },
752
+ { ALT: () => this.SUBRULE(this.annotationObject) }
753
+ ]);
754
+ });
755
+ annotationArray = this.RULE("annotationArray", () => {
756
+ this.CONSUME(LBracket);
757
+ this.MANY_SEP({
758
+ SEP: Comma,
759
+ DEF: () => {
760
+ this.SUBRULE(this.annotationValue);
761
+ }
762
+ });
763
+ this.CONSUME(RBracket);
764
+ });
765
+ annotationObject = this.RULE("annotationObject", () => {
766
+ this.CONSUME(LBrace);
767
+ this.MANY_SEP({
768
+ SEP: Comma,
769
+ DEF: () => {
770
+ this.SUBRULE(this.annotationProperty);
771
+ }
772
+ });
773
+ this.CONSUME(RBrace);
774
+ });
775
+ annotationProperty = this.RULE("annotationProperty", () => {
776
+ this.SUBRULE(this.dottedName);
777
+ this.CONSUME(Colon);
778
+ this.SUBRULE(this.annotationValue);
779
+ });
780
+ literal = this.RULE("literal", () => {
781
+ this.OR([
782
+ { ALT: () => this.CONSUME(StringLiteral) },
783
+ { ALT: () => this.CONSUME(NumberLiteral) },
784
+ { ALT: () => this.CONSUME(EnumLiteral) },
785
+ { ALT: () => this.CONSUME(True) },
786
+ { ALT: () => this.CONSUME(False) }
787
+ ]);
788
+ });
789
+ expression = this.RULE("expression", () => {
790
+ this.SUBRULE(this.comparison);
791
+ this.MANY(() => {
792
+ this.OR([{ ALT: () => this.CONSUME(And) }, { ALT: () => this.CONSUME(Or) }]);
793
+ this.SUBRULE2(this.comparison);
794
+ });
795
+ });
796
+ comparison = this.RULE("comparison", () => {
797
+ this.SUBRULE(this.operand);
798
+ this.OPTION(() => {
799
+ 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) }
807
+ ]);
808
+ this.SUBRULE2(this.operand);
809
+ });
810
+ });
811
+ operand = this.RULE("operand", () => {
812
+ this.OR([{ ALT: () => this.SUBRULE(this.literal) }, { ALT: () => this.SUBRULE(this.qualifiedName) }]);
813
+ });
814
+ typeReference = this.RULE("typeReference", () => {
815
+ this.OR([{ ALT: () => this.SUBRULE(this.builtinType) }, { ALT: () => this.SUBRULE(this.namedType) }]);
816
+ });
817
+ /** `abap.char(10)` or `abap.dec(11,2)` */
818
+ builtinType = this.RULE("builtinType", () => {
819
+ this.CONSUME(Abap);
820
+ this.CONSUME(Dot);
821
+ this.SUBRULE(this.cdsName);
822
+ this.OPTION(() => {
823
+ this.CONSUME(LParen);
824
+ this.CONSUME(NumberLiteral);
825
+ this.OPTION2(() => {
826
+ this.CONSUME(Comma);
827
+ this.CONSUME2(NumberLiteral);
828
+ });
829
+ this.CONSUME(RParen);
830
+ });
831
+ });
832
+ /** Data element or other named type reference */
833
+ namedType = this.RULE("namedType", () => {
834
+ this.SUBRULE(this.qualifiedName);
835
+ });
836
+ /** An identifier that may also be a keyword used as a name. */
837
+ 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
+ ]);
867
+ });
868
+ /** Dot-separated qualified name: foo.bar.baz */
869
+ qualifiedName = this.RULE("qualifiedName", () => {
870
+ this.SUBRULE(this.cdsName);
871
+ this.MANY(() => {
872
+ this.CONSUME(Dot);
873
+ this.SUBRULE2(this.cdsName);
874
+ });
875
+ });
876
+ };
877
+ /** Singleton parser instance (Chevrotain parsers are reusable) */
878
+ const cdsParser = new CdsParser();
879
+
880
+ //#endregion
881
+ //#region src/visitor.ts
882
+ const BaseCstVisitor = cdsParser.getBaseCstVisitorConstructor();
883
+ function asNodes(v) {
884
+ return v ?? [];
885
+ }
886
+ function asTokens(v) {
887
+ return v ?? [];
888
+ }
889
+ function collectTokenImages(node) {
890
+ if (!node) return [];
891
+ const out = [];
892
+ const walk = (n) => {
893
+ for (const children of Object.values(n.children)) for (const child of children) if ("tokenType" in child) out.push(child);
894
+ else walk(child);
895
+ };
896
+ walk(node);
897
+ out.sort((a, b) => (a.startOffset ?? 0) - (b.startOffset ?? 0));
898
+ return out;
899
+ }
900
+ function toExpression(node) {
901
+ const toks = collectTokenImages(node);
902
+ return {
903
+ source: toks.map((t) => t.image).join(" "),
904
+ tokens: toks.map((t) => t.image)
905
+ };
906
+ }
907
+ var CdsVisitor = class extends BaseCstVisitor {
908
+ constructor() {
909
+ super();
910
+ this.validateVisitor();
911
+ }
912
+ sourceFile(ctx) {
913
+ const annotations = asNodes(ctx.topLevelAnnotation).map((node) => this.visit(node));
914
+ const def = this.visit(asNodes(ctx.definition)[0]);
915
+ if ("annotations" in def) def.annotations = [...annotations, ...def.annotations];
916
+ return { definitions: [def] };
917
+ }
918
+ definition(ctx) {
919
+ if (ctx.defineStatement) return this.visit(asNodes(ctx.defineStatement)[0]);
920
+ return this.visit(asNodes(ctx.annotateStatement)[0]);
921
+ }
922
+ defineStatement(ctx) {
923
+ 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]]
932
+ ];
933
+ for (const [, node] of dispatch) if (node) return this.visit(node);
934
+ return {
935
+ kind: "table",
936
+ name: "",
937
+ annotations: [],
938
+ members: []
939
+ };
940
+ }
941
+ tableDefinition(ctx) {
942
+ return {
943
+ kind: "table",
944
+ name: this.visit(asNodes(ctx.cdsName)[0]),
945
+ annotations: [],
946
+ members: asNodes(ctx.tableMember).map((node) => this.visit(node))
947
+ };
948
+ }
949
+ structureDefinition(ctx) {
950
+ return {
951
+ kind: "structure",
952
+ name: this.visit(asNodes(ctx.cdsName)[0]),
953
+ annotations: [],
954
+ members: asNodes(ctx.tableMember).map((node) => this.visit(node))
955
+ };
956
+ }
957
+ viewEntityDefinition(ctx) {
958
+ const name = this.visit(asNodes(ctx.cdsName)[0]);
959
+ 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;
971
+ return {
972
+ kind: "viewEntity",
973
+ name,
974
+ annotations: [],
975
+ sourceKind,
976
+ source,
977
+ parameters,
978
+ members,
979
+ ...where && { where }
980
+ };
981
+ }
982
+ abstractEntityDefinition(ctx) {
983
+ 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])
989
+ };
990
+ }
991
+ customEntityDefinition(ctx) {
992
+ 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])
998
+ };
999
+ }
1000
+ selectFromClause(ctx) {
1001
+ return this.visit(asNodes(ctx.dataSource)[0]);
1002
+ }
1003
+ projectionOnClause(ctx) {
1004
+ return this.visit(asNodes(ctx.dataSource)[0]);
1005
+ }
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;
1010
+ return {
1011
+ name,
1012
+ ...alias && { alias }
1013
+ };
1014
+ }
1015
+ elementList(ctx) {
1016
+ return asNodes(ctx.elementMember).map((node) => this.visit(node));
1017
+ }
1018
+ elementMember(ctx) {
1019
+ const annotations = asNodes(ctx.annotation).map((n) => this.visit(n));
1020
+ if (ctx.associationTail) {
1021
+ const decl = this.visit(asNodes(ctx.associationTail)[0]);
1022
+ decl.annotations = annotations;
1023
+ return decl;
1024
+ }
1025
+ const el = this.visit(asNodes(ctx.viewElementTail)[0]);
1026
+ el.annotations = annotations;
1027
+ return el;
1028
+ }
1029
+ viewElementTail(ctx) {
1030
+ const isKey = !!asTokens(ctx.Key).length;
1031
+ const isVirtual = !!asTokens(ctx.Virtual).length;
1032
+ const isRedirected = !!asTokens(ctx.Redirected).length;
1033
+ if (ctx.typedFieldTail) {
1034
+ const tail = this.visit(asNodes(ctx.typedFieldTail)[0]);
1035
+ return {
1036
+ annotations: [],
1037
+ isKey,
1038
+ isVirtual,
1039
+ isRedirected,
1040
+ expression: tail.name,
1041
+ type: tail.type,
1042
+ notNull: tail.notNull
1043
+ };
1044
+ }
1045
+ const proj = this.visit(asNodes(ctx.projectionExpression)[0]);
1046
+ return {
1047
+ annotations: [],
1048
+ isKey,
1049
+ isVirtual,
1050
+ isRedirected,
1051
+ expression: proj.expression,
1052
+ ...proj.alias && { alias: proj.alias }
1053
+ };
1054
+ }
1055
+ typedFieldTail(ctx) {
1056
+ return {
1057
+ name: this.visit(asNodes(ctx.cdsName)[0]),
1058
+ type: this.visit(asNodes(ctx.typeReference)[0]),
1059
+ notNull: !!asTokens(ctx.Not).length
1060
+ };
1061
+ }
1062
+ projectionExpression(ctx) {
1063
+ const expression = this.visit(asNodes(ctx.qualifiedName)[0]);
1064
+ const aliasNode = asNodes(ctx.cdsName)[0];
1065
+ const alias = aliasNode ? this.visit(aliasNode) : void 0;
1066
+ return {
1067
+ expression,
1068
+ ...alias && { alias }
1069
+ };
1070
+ }
1071
+ associationTail(ctx) {
1072
+ 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";
1077
+ const redirected = !!asTokens(ctx.Redirected).length;
1078
+ const target = this.visit(asNodes(ctx.qualifiedName)[0]);
1079
+ const aliasNode = asNodes(ctx.cdsName)[0];
1080
+ const alias = aliasNode ? this.visit(aliasNode) : void 0;
1081
+ const on = ctx.expression ? toExpression(asNodes(ctx.expression)[0]) : void 0;
1082
+ return {
1083
+ kind: "association",
1084
+ associationKind,
1085
+ annotations: [],
1086
+ ...cardinality && { cardinality },
1087
+ ...targetMultiplicity && { targetMultiplicity },
1088
+ target,
1089
+ ...redirected && { redirected: true },
1090
+ ...alias && { alias },
1091
+ ...on && { on }
1092
+ };
1093
+ }
1094
+ cardinality(ctx) {
1095
+ const nums = asTokens(ctx.NumberLiteral);
1096
+ const stars = asTokens(ctx.Star);
1097
+ if (nums.length === 0 && stars.length >= 1) return { max: "*" };
1098
+ if (nums.length === 1) {
1099
+ if (asTokens(ctx.Dot).length === 0) return { max: Number.parseInt(nums[0].image, 10) };
1100
+ if (stars.length >= 1) return {
1101
+ min: Number.parseInt(nums[0].image, 10),
1102
+ max: "*"
1103
+ };
1104
+ return {
1105
+ min: Number.parseInt(nums[0].image, 10),
1106
+ max: "*"
1107
+ };
1108
+ }
1109
+ if (nums.length === 2) return {
1110
+ min: Number.parseInt(nums[0].image, 10),
1111
+ max: Number.parseInt(nums[1].image, 10)
1112
+ };
1113
+ return { max: "*" };
1114
+ }
1115
+ parametersClause(ctx) {
1116
+ return asNodes(ctx.parameterDefinition).map((n) => this.visit(n));
1117
+ }
1118
+ parameterDefinition(ctx) {
1119
+ const annotations = asNodes(ctx.annotation).map((n) => this.visit(n));
1120
+ const name = this.visit(asNodes(ctx.cdsName)[0]);
1121
+ const type = this.visit(asNodes(ctx.typeReference)[0]);
1122
+ const defaultValue = ctx.literal ? this.visit(asNodes(ctx.literal)[0]) : void 0;
1123
+ return {
1124
+ annotations,
1125
+ name,
1126
+ type,
1127
+ ...defaultValue && { defaultValue }
1128
+ };
1129
+ }
1130
+ tableMember(ctx) {
1131
+ if (ctx.includeDirective) return this.visit(asNodes(ctx.includeDirective)[0]);
1132
+ return this.visit(asNodes(ctx.fieldDefinition)[0]);
1133
+ }
1134
+ includeDirective(ctx) {
1135
+ const name = this.visit(asNodes(ctx.cdsName)[0]);
1136
+ const suffix = asNodes(ctx.cdsName)[1] ? this.visit(asNodes(ctx.cdsName)[1]) : void 0;
1137
+ return {
1138
+ kind: "include",
1139
+ name,
1140
+ ...suffix !== void 0 && { suffix }
1141
+ };
1142
+ }
1143
+ fieldDefinition(ctx) {
1144
+ const annotations = asNodes(ctx.annotation).map((node) => this.visit(node));
1145
+ const isKey = !!asTokens(ctx.Key).length;
1146
+ return {
1147
+ annotations,
1148
+ name: this.visit(asNodes(ctx.cdsName)[0]),
1149
+ type: this.visit(asNodes(ctx.typeReference)[0]),
1150
+ isKey,
1151
+ notNull: !!asTokens(ctx.Not).length
1152
+ };
1153
+ }
1154
+ simpleTypeDefinition(ctx) {
1155
+ return {
1156
+ kind: "simpleType",
1157
+ name: this.visit(asNodes(ctx.cdsName)[0]),
1158
+ annotations: [],
1159
+ type: this.visit(asNodes(ctx.typeReference)[0])
1160
+ };
1161
+ }
1162
+ serviceDefinition(ctx) {
1163
+ return {
1164
+ kind: "service",
1165
+ name: this.visit(asNodes(ctx.cdsName)[0]),
1166
+ annotations: [],
1167
+ exposes: asNodes(ctx.exposeStatement).map((node) => this.visit(node))
1168
+ };
1169
+ }
1170
+ exposeStatement(ctx) {
1171
+ const names = asNodes(ctx.cdsName).map((n) => this.visit(n));
1172
+ const entity = names[0];
1173
+ const alias = names.length > 1 ? names[1] : void 0;
1174
+ return {
1175
+ entity,
1176
+ ...alias && { alias }
1177
+ };
1178
+ }
1179
+ roleDefinition(ctx) {
1180
+ return {
1181
+ kind: "role",
1182
+ name: this.visit(asNodes(ctx.cdsName)[0]),
1183
+ annotations: [],
1184
+ grants: asNodes(ctx.grantStatement).map((n) => this.visit(n))
1185
+ };
1186
+ }
1187
+ 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 {
1191
+ privilege: "select",
1192
+ entity,
1193
+ ...where && { where }
1194
+ };
1195
+ }
1196
+ annotateStatement(ctx) {
1197
+ return {
1198
+ kind: "metadataExtension",
1199
+ entity: this.visit(asNodes(ctx.cdsName)[0]),
1200
+ annotations: [],
1201
+ elements: asNodes(ctx.annotatedElement).map((node) => this.visit(node))
1202
+ };
1203
+ }
1204
+ annotatedElement(ctx) {
1205
+ return {
1206
+ annotations: asNodes(ctx.annotation).map((node) => this.visit(node)),
1207
+ name: this.visit(asNodes(ctx.cdsName)[0])
1208
+ };
1209
+ }
1210
+ topLevelAnnotation(ctx) {
1211
+ return this.visit(asNodes(ctx.annotation)[0]);
1212
+ }
1213
+ annotation(ctx) {
1214
+ return {
1215
+ key: this.visit(asNodes(ctx.dottedName)[0]),
1216
+ value: ctx.annotationValue ? this.visit(asNodes(ctx.annotationValue)[0]) : {
1217
+ kind: "boolean",
1218
+ value: true
1219
+ }
1220
+ };
1221
+ }
1222
+ dottedName(ctx) {
1223
+ return asNodes(ctx.cdsName).map((node) => this.visit(node)).join(".");
1224
+ }
1225
+ annotationValue(ctx) {
1226
+ if (ctx.StringLiteral) return {
1227
+ kind: "string",
1228
+ value: asTokens(ctx.StringLiteral)[0].image.slice(1, -1)
1229
+ };
1230
+ if (ctx.EnumLiteral) return {
1231
+ kind: "enum",
1232
+ value: asTokens(ctx.EnumLiteral)[0].image.slice(1)
1233
+ };
1234
+ if (ctx.True) return {
1235
+ kind: "boolean",
1236
+ value: true
1237
+ };
1238
+ if (ctx.False) return {
1239
+ kind: "boolean",
1240
+ value: false
1241
+ };
1242
+ if (ctx.NumberLiteral) return {
1243
+ kind: "number",
1244
+ value: parseFloat(asTokens(ctx.NumberLiteral)[0].image)
1245
+ };
1246
+ if (ctx.annotationArray) return this.visit(asNodes(ctx.annotationArray)[0]);
1247
+ return this.visit(asNodes(ctx.annotationObject)[0]);
1248
+ }
1249
+ annotationArray(ctx) {
1250
+ return {
1251
+ kind: "array",
1252
+ items: asNodes(ctx.annotationValue).map((node) => this.visit(node))
1253
+ };
1254
+ }
1255
+ annotationObject(ctx) {
1256
+ return {
1257
+ kind: "object",
1258
+ properties: asNodes(ctx.annotationProperty).map((node) => this.visit(node))
1259
+ };
1260
+ }
1261
+ annotationProperty(ctx) {
1262
+ return {
1263
+ key: this.visit(asNodes(ctx.dottedName)[0]),
1264
+ value: this.visit(asNodes(ctx.annotationValue)[0])
1265
+ };
1266
+ }
1267
+ literal(ctx) {
1268
+ if (ctx.StringLiteral) return {
1269
+ kind: "string",
1270
+ value: asTokens(ctx.StringLiteral)[0].image.slice(1, -1)
1271
+ };
1272
+ if (ctx.EnumLiteral) return {
1273
+ kind: "enum",
1274
+ value: asTokens(ctx.EnumLiteral)[0].image.slice(1)
1275
+ };
1276
+ if (ctx.True) return {
1277
+ kind: "boolean",
1278
+ value: true
1279
+ };
1280
+ if (ctx.False) return {
1281
+ kind: "boolean",
1282
+ value: false
1283
+ };
1284
+ return {
1285
+ kind: "number",
1286
+ value: parseFloat(asTokens(ctx.NumberLiteral)[0].image)
1287
+ };
1288
+ }
1289
+ expression() {
1290
+ return null;
1291
+ }
1292
+ comparison() {
1293
+ return null;
1294
+ }
1295
+ operand() {
1296
+ return null;
1297
+ }
1298
+ typeReference(ctx) {
1299
+ if (ctx.builtinType) return this.visit(asNodes(ctx.builtinType)[0]);
1300
+ return this.visit(asNodes(ctx.namedType)[0]);
1301
+ }
1302
+ builtinType(ctx) {
1303
+ const name = this.visit(asNodes(ctx.cdsName)[0]);
1304
+ const numberTokens = asTokens(ctx.NumberLiteral);
1305
+ return {
1306
+ kind: "builtin",
1307
+ name,
1308
+ length: numberTokens[0] ? Number.parseInt(numberTokens[0].image, 10) : void 0,
1309
+ decimals: numberTokens[1] ? Number.parseInt(numberTokens[1].image, 10) : void 0
1310
+ };
1311
+ }
1312
+ namedType(ctx) {
1313
+ return {
1314
+ kind: "named",
1315
+ name: this.visit(asNodes(ctx.qualifiedName)[0])
1316
+ };
1317
+ }
1318
+ cdsName(ctx) {
1319
+ const t = ctx;
1320
+ for (const key of Object.keys(t)) {
1321
+ const tok = t[key]?.[0];
1322
+ if (tok && "image" in tok) return tok.image;
1323
+ }
1324
+ return "";
1325
+ }
1326
+ qualifiedName(ctx) {
1327
+ return asNodes(ctx.cdsName).map((node) => this.visit(node)).join(".");
1328
+ }
1329
+ };
1330
+ /** Singleton visitor instance */
1331
+ const cdsVisitor = new CdsVisitor();
1332
+
1333
+ //#endregion
1334
+ //#region src/errors.ts
1335
+ function fromLexError(err) {
1336
+ return {
1337
+ message: err.message,
1338
+ line: err.line ?? 1,
1339
+ column: err.column ?? 1,
1340
+ offset: err.offset,
1341
+ length: err.length
1342
+ };
1343
+ }
1344
+ function fromParseError(err) {
1345
+ const token = err.token;
1346
+ return {
1347
+ message: err.message,
1348
+ line: token.startLine ?? 1,
1349
+ column: token.startColumn ?? 1,
1350
+ offset: token.startOffset,
1351
+ length: (token.endOffset ?? token.startOffset) - token.startOffset + 1
1352
+ };
1353
+ }
1354
+
1355
+ //#endregion
1356
+ //#region src/lib/ast/walker.ts
1357
+ /** True when the definition has a `members` array (view-like entities). */
1358
+ function hasMembers(def) {
1359
+ return def.kind === "viewEntity" || def.kind === "abstractEntity" || def.kind === "customEntity";
1360
+ }
1361
+ /** True when the definition has a `members` array of typed fields (TABL/structure). */
1362
+ function hasFields(def) {
1363
+ return def.kind === "table" || def.kind === "structure";
1364
+ }
1365
+ function hasParameters(def) {
1366
+ return def.kind === "viewEntity" || def.kind === "abstractEntity" || def.kind === "customEntity";
1367
+ }
1368
+ /** Yield every definition in the file (currently one, future-proofed). */
1369
+ function* walkDefinitions(file) {
1370
+ for (const def of file.definitions) yield def;
1371
+ }
1372
+ /**
1373
+ * Yield every annotation attached anywhere in the AST (definitions,
1374
+ * elements/fields, parameters, associations).
1375
+ */
1376
+ function* walkAnnotations(file) {
1377
+ for (const def of walkDefinitions(file)) {
1378
+ 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;
1384
+ }
1385
+ }
1386
+ /** Safe accessor — returns an empty array if the definition carries none. */
1387
+ function getAnnotations(def) {
1388
+ return "annotations" in def ? def.annotations : [];
1389
+ }
1390
+ /** Yield every element (`ViewElement`) across all view-like definitions. */
1391
+ function* walkViewElements(file) {
1392
+ for (const def of walkDefinitions(file)) {
1393
+ if (!hasMembers(def)) continue;
1394
+ for (const m of def.members) {
1395
+ if (isAssociation(m)) continue;
1396
+ yield {
1397
+ owner: def,
1398
+ element: m
1399
+ };
1400
+ }
1401
+ }
1402
+ }
1403
+ /** Yield every association declaration across view-like definitions. */
1404
+ function* walkAssociations(file) {
1405
+ for (const def of walkDefinitions(file)) {
1406
+ 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
+ }
1414
+ }
1415
+ }
1416
+ /** Yield every field definition from tables and structures. */
1417
+ function* walkFields(file) {
1418
+ for (const def of walkDefinitions(file)) {
1419
+ if (!hasFields(def)) continue;
1420
+ for (const m of def.members) {
1421
+ if ("kind" in m && m.kind === "include") continue;
1422
+ yield {
1423
+ owner: def,
1424
+ field: m
1425
+ };
1426
+ }
1427
+ }
1428
+ }
1429
+ /** Yield every parameter definition across view-like entities. */
1430
+ function* walkParameters(file) {
1431
+ for (const def of walkDefinitions(file)) {
1432
+ if (!hasParameters(def)) continue;
1433
+ for (const p of def.parameters) yield {
1434
+ owner: def,
1435
+ parameter: p
1436
+ };
1437
+ }
1438
+ }
1439
+ function isAssociation(m) {
1440
+ return "kind" in m && m.kind === "association";
1441
+ }
1442
+ /** Look up an annotation by its dotted key. Returns the first match. */
1443
+ function findAnnotation(annotations, key) {
1444
+ return annotations.find((a) => a.key === key);
1445
+ }
1446
+
1447
+ //#endregion
1448
+ //#region src/lib/validate/cardinality.ts
1449
+ function validateCardinality(file) {
1450
+ const diagnostics = [];
1451
+ for (const { association } of walkAssociations(file)) {
1452
+ const card = association.cardinality;
1453
+ if (!card) continue;
1454
+ const { min, max } = card;
1455
+ const name = association.alias ?? association.target;
1456
+ if (min !== void 0 && min < 0) diagnostics.push({
1457
+ code: "ACDS001",
1458
+ severity: "error",
1459
+ message: `Association '${name}': lower bound (${min}) must be >= 0.`,
1460
+ target: name
1461
+ });
1462
+ if (typeof max === "number" && max < 1) diagnostics.push({
1463
+ code: "ACDS002",
1464
+ severity: "error",
1465
+ message: `Association '${name}': upper bound (${max}) must be >= 1.`,
1466
+ target: name
1467
+ });
1468
+ if (min !== void 0 && typeof max === "number" && min > max) diagnostics.push({
1469
+ code: "ACDS003",
1470
+ severity: "error",
1471
+ message: `Association '${name}': lower bound (${min}) exceeds upper bound (${max}).`,
1472
+ target: name
1473
+ });
1474
+ }
1475
+ return diagnostics;
1476
+ }
1477
+
1478
+ //#endregion
1479
+ //#region src/lib/validate/elements.ts
1480
+ function validateViewElements(file) {
1481
+ const diagnostics = [];
1482
+ for (const { element } of walkViewElements(file)) {
1483
+ const name = element.alias ?? element.expression;
1484
+ if (element.isVirtual && element.type) diagnostics.push({
1485
+ code: "ACDS010",
1486
+ severity: "error",
1487
+ message: `View element '${name}' cannot be virtual and typed at the same time.`,
1488
+ target: name
1489
+ });
1490
+ if (element.isKey && element.isVirtual) diagnostics.push({
1491
+ code: "ACDS011",
1492
+ severity: "error",
1493
+ message: `View element '${name}' cannot be both 'key' and 'virtual'.`,
1494
+ target: name
1495
+ });
1496
+ }
1497
+ return diagnostics;
1498
+ }
1499
+
1500
+ //#endregion
1501
+ //#region src/lib/validate/index.ts
1502
+ /** Run all validators. */
1503
+ function validate(file) {
1504
+ return [...validateCardinality(file), ...validateViewElements(file)];
1505
+ }
1506
+
1507
+ //#endregion
1508
+ //#region src/lib/grammar/ddl.ts
1509
+ const ddlCoverage = {
1510
+ topic: "ddl",
1511
+ constructs: [
1512
+ "define table",
1513
+ "define structure",
1514
+ "define type",
1515
+ "define view entity",
1516
+ "define abstract entity",
1517
+ "define custom entity",
1518
+ "define service",
1519
+ "annotate entity",
1520
+ "include directive",
1521
+ "builtin type (abap.char, abap.dec, ...)",
1522
+ "named type reference"
1523
+ ]
1524
+ };
1525
+
1526
+ //#endregion
1527
+ //#region src/lib/grammar/dcl.ts
1528
+ const dclCoverage = {
1529
+ topic: "dcl",
1530
+ constructs: [
1531
+ "define role <name> { ... }",
1532
+ "grant select on <entity>",
1533
+ "grant select on <entity> where <expression>"
1534
+ ]
1535
+ };
1536
+
1537
+ //#endregion
1538
+ //#region src/lib/grammar/annotations.ts
1539
+ const annotationsCoverage = {
1540
+ topic: "annotations",
1541
+ constructs: [
1542
+ "@Key : value",
1543
+ "@Key.path : value",
1544
+ "array value",
1545
+ "object value",
1546
+ "nested objects",
1547
+ "bare boolean (@Key)"
1548
+ ]
1549
+ };
1550
+
1551
+ //#endregion
1552
+ //#region src/lib/grammar/associations.ts
1553
+ const associationsCoverage = {
1554
+ topic: "associations",
1555
+ constructs: [
1556
+ "association to <target>",
1557
+ "association[cardinality] to <target>",
1558
+ "association[L..U] to <target>",
1559
+ "association of (many|one) to <target>",
1560
+ "composition to <target>",
1561
+ "association to redirected to <target>",
1562
+ "association ... as <alias>",
1563
+ "association ... on <expression>"
1564
+ ]
1565
+ };
1566
+
1567
+ //#endregion
1568
+ //#region src/lib/grammar/parameters.ts
1569
+ const parametersCoverage = {
1570
+ topic: "parameters",
1571
+ constructs: [
1572
+ "with parameters <p>: <type>",
1573
+ "with parameters <p>: <type> default <literal>",
1574
+ "annotated parameters"
1575
+ ]
1576
+ };
1577
+
1578
+ //#endregion
1579
+ //#region src/lib/grammar/projections.ts
1580
+ const projectionsCoverage = {
1581
+ topic: "projections",
1582
+ constructs: [
1583
+ "view element (qualified name)",
1584
+ "view element with alias",
1585
+ "view element with key modifier",
1586
+ "view element with virtual modifier",
1587
+ "typed field (abstract/custom entity)",
1588
+ "view where clause",
1589
+ "projection view (as projection on <source>)"
1590
+ ]
1591
+ };
1592
+
1593
+ //#endregion
1594
+ //#region src/lib/grammar/actions.ts
1595
+ const actionsCoverage = {
1596
+ topic: "actions",
1597
+ constructs: []
1598
+ };
1599
+
1600
+ //#endregion
1601
+ //#region src/lib/grammar/index.ts
1602
+ const GRAMMAR_COVERAGE = [
1603
+ ddlCoverage,
1604
+ dclCoverage,
1605
+ annotationsCoverage,
1606
+ associationsCoverage,
1607
+ parametersCoverage,
1608
+ projectionsCoverage,
1609
+ actionsCoverage
1610
+ ];
1611
+
1612
+ //#endregion
1613
+ //#region src/index.ts
1614
+ /**
1615
+ * @abapify/acds — ABAP CDS Source Parser
1616
+ *
1617
+ * Parses ABAP CDS DDL source (.acds) files into a typed AST.
1618
+ *
1619
+ * @example
1620
+ * ```typescript
1621
+ * import { parse } from '@abapify/acds';
1622
+ *
1623
+ * const result = parse(`
1624
+ * @AbapCatalog.tableCategory : #TRANSPARENT
1625
+ * define table ztable {
1626
+ * key field1 : abap.char(10) not null;
1627
+ * field2 : some_data_element;
1628
+ * }
1629
+ * `);
1630
+ *
1631
+ * if (result.errors.length === 0) {
1632
+ * const table = result.ast.definitions[0]; // TableDefinition
1633
+ * }
1634
+ * ```
1635
+ */
1636
+ /**
1637
+ * Parse an ABAP CDS source string into a typed AST.
1638
+ *
1639
+ * @param source - The CDS source code to parse
1640
+ * @returns Parse result with AST and any errors
1641
+ */
1642
+ function parse(source) {
1643
+ const lexResult = CdsLexer.tokenize(source);
1644
+ const errors = lexResult.errors.map(fromLexError);
1645
+ cdsParser.input = lexResult.tokens;
1646
+ const cst = cdsParser.sourceFile();
1647
+ errors.push(...cdsParser.errors.map(fromParseError));
1648
+ let ast = { definitions: [] };
1649
+ if (cst) try {
1650
+ ast = cdsVisitor.visit(cst);
1651
+ } catch {}
1652
+ return {
1653
+ ast,
1654
+ errors
1655
+ };
1656
+ }
1657
+
1658
+ //#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 };
1660
+ //# sourceMappingURL=index.mjs.map