@abapify/acds 0.4.15 → 0.4.17
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/README.md +153 -14
- package/dist/index.d.mts +427 -8
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3091 -245
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
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:
|
|
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([
|
|
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
|
-
|
|
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.
|
|
1273
|
+
this.OPTION({
|
|
1274
|
+
GATE: () => this.isEntityModifier(),
|
|
1275
|
+
DEF: () => this.CONSUME(Entity)
|
|
1276
|
+
});
|
|
471
1277
|
this.SUBRULE(this.cdsName);
|
|
472
|
-
this.
|
|
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(
|
|
482
|
-
this.
|
|
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.
|
|
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.
|
|
1328
|
+
this.memberContext = "entity";
|
|
1329
|
+
try {
|
|
1330
|
+
this.SUBRULE(this.elementList);
|
|
1331
|
+
} finally {
|
|
1332
|
+
this.memberContext = "view";
|
|
1333
|
+
}
|
|
502
1334
|
});
|
|
503
|
-
|
|
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(
|
|
1384
|
+
this.CONSUME(Where);
|
|
1385
|
+
this.SUBRULE(this.condition);
|
|
507
1386
|
});
|
|
508
|
-
this.
|
|
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
|
-
|
|
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.
|
|
519
|
-
|
|
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([{
|
|
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(
|
|
535
|
-
|
|
536
|
-
|
|
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.
|
|
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
|
|
556
|
-
*
|
|
557
|
-
*
|
|
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
|
-
|
|
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
|
-
/**
|
|
567
|
-
|
|
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
|
|
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([
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
this.
|
|
580
|
-
this.
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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.
|
|
1902
|
+
this.OPTION7(() => {
|
|
595
1903
|
this.CONSUME(On);
|
|
596
|
-
this.SUBRULE(this.
|
|
1904
|
+
this.SUBRULE(this.condition);
|
|
597
1905
|
});
|
|
598
|
-
this.
|
|
599
|
-
this.
|
|
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.
|
|
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.
|
|
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([{
|
|
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.
|
|
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.
|
|
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(
|
|
801
|
-
{ ALT: () => this.CONSUME(
|
|
802
|
-
{ ALT: () => this.CONSUME(
|
|
803
|
-
{ ALT: () => this.CONSUME(
|
|
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
|
-
|
|
812
|
-
|
|
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([{
|
|
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
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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
|
|
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
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
3223
|
+
associations,
|
|
3224
|
+
redefinedAssociations,
|
|
3225
|
+
aspectBindings,
|
|
978
3226
|
members,
|
|
979
3227
|
...where && { where }
|
|
980
3228
|
};
|
|
981
3229
|
}
|
|
982
|
-
|
|
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
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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
|
-
|
|
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
|
-
|
|
994
|
-
|
|
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
|
-
|
|
1001
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1012
|
-
|
|
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.
|
|
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
|
-
|
|
1063
|
-
const expression =
|
|
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 =
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
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
|
|
3743
|
+
name,
|
|
1183
3744
|
annotations: [],
|
|
1184
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
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:
|
|
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
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
if (
|
|
1383
|
-
if (hasParameters(def))
|
|
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
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
"
|
|
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
|