@sap/cds-compiler 6.3.6 → 6.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +32 -0
  3. package/README.md +14 -2
  4. package/bin/cdsse.js +0 -3
  5. package/doc/CHANGELOG_BETA.md +1 -1
  6. package/doc/CHANGELOG_DEPRECATED.md +1 -1
  7. package/lib/base/message-registry.js +7 -0
  8. package/lib/base/messages.js +1 -1
  9. package/lib/base/model.js +2 -0
  10. package/lib/compiler/assert-consistency.js +1 -0
  11. package/lib/compiler/checks.js +37 -26
  12. package/lib/compiler/define.js +1 -1
  13. package/lib/compiler/extend.js +39 -50
  14. package/lib/compiler/finalize-parse-cdl.js +1 -1
  15. package/lib/compiler/lsp-api.js +1 -1
  16. package/lib/compiler/populate.js +2 -2
  17. package/lib/compiler/propagator.js +29 -6
  18. package/lib/compiler/resolve.js +13 -3
  19. package/lib/compiler/shared.js +31 -25
  20. package/lib/compiler/tweak-assocs.js +86 -28
  21. package/lib/compiler/xpr-rewrite.js +70 -38
  22. package/lib/edm/annotations/edmJson.js +206 -37
  23. package/lib/edm/csn2edm.js +13 -0
  24. package/lib/edm/edmUtils.js +2 -2
  25. package/lib/gen/BaseParser.js +106 -72
  26. package/lib/gen/CdlGrammar.checksum +1 -1
  27. package/lib/gen/CdlParser.js +1500 -1509
  28. package/lib/json/to-csn.js +8 -5
  29. package/lib/language/genericAntlrParser.js +0 -0
  30. package/lib/main.js +19 -16
  31. package/lib/model/csnRefs.js +589 -521
  32. package/lib/model/csnUtils.js +8 -5
  33. package/lib/model/enrichCsn.js +1 -0
  34. package/lib/parsers/AstBuildingParser.js +72 -27
  35. package/lib/render/toCdl.js +2 -1
  36. package/lib/render/toHdbcds.js +6 -3
  37. package/lib/render/toSql.js +5 -0
  38. package/lib/transform/db/applyTransformations.js +1 -1
  39. package/lib/transform/db/assertUnique.js +4 -1
  40. package/lib/transform/db/cdsPersistence.js +17 -18
  41. package/lib/transform/db/expansion.js +179 -3
  42. package/lib/transform/db/flattening.js +16 -5
  43. package/lib/transform/db/rewriteCalculatedElements.js +79 -283
  44. package/lib/transform/effective/main.js +8 -1
  45. package/lib/transform/forOdata.js +1 -1
  46. package/lib/transform/forRelationalDB.js +21 -80
  47. package/lib/transform/localized.js +65 -110
  48. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +89 -63
  49. package/lib/transform/transformUtils.js +23 -21
  50. package/lib/transform/translateAssocsToJoins.js +7 -5
  51. package/lib/transform/tupleExpansion.js +16 -3
  52. package/package.json +1 -1
  53. package/doc/DeprecatedOptions_v2.md +0 -150
  54. package/doc/NameResolution.md +0 -837
  55. package/lib/transform/parseExpr.js +0 -415
@@ -1,415 +0,0 @@
1
- // @ts-nocheck
2
-
3
- 'use strict';
4
-
5
- /**
6
- * parseExpr() accepts any JSON object and tries to convert a token stream expression
7
- * array into an AST like expression with CDL operator precedence.
8
- *
9
- * The following operators are supported:
10
- *
11
- * Unary: +/-
12
- * Multiplication/Division: '*', '/'
13
- * Addition/Subtraction: '+', '-'
14
- * Concatenation: '||'
15
- * Relational: '=', '<>', '>', '>=', '<', '<=', '==', '!=', 'like', 'in', 'exists', 'between and'
16
- * Unary: 'is [not] null', 'not'
17
- * Conditional: 'case [when then]+ [else]? end', 'and', 'or'
18
- *
19
- * stand-alone token: 'new'
20
- *
21
- * This is not an optimized LL(1) parser but a token 'sniffer'. A stream is
22
- * cracked up in sub streams and passed down to the next higher function.
23
- *
24
- * Complex aggregates like case/when/else/end and between are parsed first to pass down the
25
- * resulting sub expressions and avoiding 'and' ambiguities.
26
- *
27
- * Sub expressions are grouped as arrays, the final AST is an array of nested arrays.
28
- * Alternatively, an object like AST can be produced by setting argument 'array' to false.
29
- *
30
- * This parser intentionally does no error handling. If a clause is malformed, it is accepted as is.
31
- *
32
- * @param {any} xpr A JSON object.
33
- * @param {Object} state Object
34
- * anno: Don't eliminate arrays with single entry in expressions (TODO?) as they are collections
35
- * array: Bias AST representation.
36
- * nary: return n-ary or binary tree
37
- */
38
-
39
- function parseExpr(xpr, state = { array: true, nary: false }) {
40
- state.anno = 0;
41
- // Notes:
42
- // - Variables `s` and `e` are used as index variables into `xpr`s for start and end.
43
- // - xpr's are our CSN expressions, see <https://cap.cloud.sap/docs/cds/cxn>
44
-
45
- return parseExprInt(xpr, state);
46
-
47
- function parseExprInt(xpr, state) {
48
- return conditionOR(...CaseWhen(Cast(xpr, state)), state);
49
- }
50
-
51
- function Cast(xpr, state) {
52
- if (xpr != null && !state.array) {
53
- if (Array.isArray(xpr))
54
- return xpr.map(x => Cast(x, state));
55
- if (typeof xpr === 'object') {
56
- const castKeys = Object.keys(xpr).filter(k => k !== 'cast');
57
- if (xpr.cast != null && castKeys.length === 1)
58
- return { cast: [ xpr.cast, { [castKeys[0]]: xpr[castKeys[0]] } ] };
59
-
60
-
61
- for (const n in xpr) {
62
- // xpr could be an array with polluted prototype
63
- if (Object.hasOwnProperty.call(xpr, n))
64
- xpr[n] = Cast(xpr[n], state);
65
- }
66
- }
67
- }
68
- return xpr;
69
- }
70
-
71
- function CaseWhen(xpr) {
72
- if (Array.isArray(xpr))
73
- recurseIntoCases();
74
-
75
- return [ xpr, 0, Array.isArray(xpr) ? xpr.length : 1 ];
76
-
77
- function recurseIntoCases(casePos = -1, lvl = -1) {
78
- for (let c = casePos + 1; c < xpr.length; c++) {
79
- if (xpr[c] === 'case')
80
- recurseIntoCases(c, lvl + 1);
81
- }
82
- if (lvl > -1) {
83
- let endPos = casePos;
84
- while (xpr[endPos] !== 'end' && endPos < xpr.length)
85
- endPos++;
86
- if (xpr[endPos] === 'end') {
87
- const caseTree = rewriteCaseBlock(casePos, endPos);
88
- if (casePos === 0 && endPos === xpr.length - 1)
89
- xpr = caseTree;
90
- else
91
- xpr.splice(casePos, endPos - casePos + 1, caseTree);
92
- }
93
- }
94
- }
95
-
96
- /**
97
- * @param {number} casePos
98
- * @param {number} endPos
99
- * @return {Array|object}
100
- */
101
- function rewriteCaseBlock(casePos, endPos) {
102
- const caseTree = state.array ? [ 'case' ] : { case: [] };
103
-
104
- let elsePos = endPos;
105
- let whenPos = casePos;
106
-
107
- while (xpr[elsePos] !== 'else' && elsePos > casePos)
108
- elsePos--;
109
- let elseCond;
110
- if (xpr[elsePos] === 'else') {
111
- elseCond = xpr.slice(elsePos + 1, endPos);
112
- endPos = elsePos;
113
- }
114
-
115
- while (xpr[whenPos] !== 'when' && whenPos < endPos)
116
- whenPos++;
117
- if (xpr[whenPos] === 'when' && whenPos - (casePos + 1) >= 1) {
118
- const caseExpr = xpr.slice(casePos + 1, whenPos);
119
- if (state.array)
120
- caseTree.push(caseExpr);
121
- else
122
- caseTree.case.push(caseExpr.length === 1 ? caseExpr[0] : caseExpr);
123
- }
124
-
125
- while (xpr[whenPos] === 'when') {
126
- const when = { when: [] };
127
- if (state.array)
128
- caseTree.push('when');
129
- else
130
- caseTree.case.push(when);
131
-
132
- let thenPos = whenPos + 1;
133
- while (xpr[thenPos] !== 'then' && thenPos < endPos)
134
- thenPos++;
135
- if (xpr[thenPos] === 'then') {
136
- const whenExpr = xpr.slice(whenPos + 1, thenPos);
137
- if (state.array)
138
- caseTree.push(whenExpr);
139
- else
140
- when.when.push(whenExpr.length === 1 ? whenExpr[0] : whenExpr);
141
- }
142
-
143
- whenPos = thenPos + 1;
144
- while (xpr[whenPos] !== 'when' && whenPos < endPos)
145
- whenPos++;
146
- if (xpr[whenPos] === 'when' || whenPos === endPos) {
147
- const then = xpr.slice(thenPos + 1, whenPos);
148
- if (state.array)
149
- caseTree.push('then', then);
150
- else
151
- when.when.push(then.length === 1 ? then[0] : then);
152
- }
153
- }
154
- if (elseCond) {
155
- if (state.array)
156
- caseTree.push('else', elseCond);
157
- else
158
- caseTree.case.push(elseCond.length === 1 ? elseCond[0] : elseCond);
159
- }
160
- if (state.array)
161
- caseTree.push('end');
162
- return caseTree;
163
- }
164
- }
165
-
166
- function conditionOR(xpr, s, e, state) {
167
- return binaryExpr(xpr, [ 'or' ], conditionAnd, s, e, state);
168
- }
169
-
170
- function conditionAnd(xpr, s, e, state) {
171
- return binaryExpr(xpr, (xpr, s, e) => {
172
- let a = s - 1;
173
- let b;
174
- do {
175
- b = false;
176
- for (a++; xpr[a] !== 'and' && a < e; a++) {
177
- if (xpr[a] === 'between')
178
- b = true;
179
- }
180
- } while (b && a < e);
181
-
182
- if (!b && a < e)
183
- return [ 1, a ];
184
- return [ 1, -1 ];
185
- }, conditionTerm, s, e, state);
186
- }
187
-
188
- function conditionTerm(xpr, s, e, state) {
189
- if (Array.isArray(xpr)) {
190
- if (xpr.length >= 3 && xpr[s + 1] === 'is') {
191
- const isnull = conditionOR(xpr[s], 0, 0, state);
192
- if (xpr[s + 2] === 'null')
193
- return state.array ? [ isnull, 'is', 'null' ] : { isNull: isnull };
194
- else if (xpr[s + 2] === 'not' && xpr[s + 3] === 'null')
195
- return state.array ? [ isnull, 'is', 'not', 'null' ] : { isNotNull: isnull };
196
- }
197
- if (xpr[s] === 'not') {
198
- const not = conditionTerm(xpr, s + 1, e, state);
199
- return state.array ? [ 'not', not ] : { not };
200
- }
201
- if (xpr[s] === 'exists') {
202
- const exists = conditionTerm(xpr, s + 1, e, state);
203
- return state.array ? [ 'exists', exists ] : { exists };
204
- }
205
- }
206
- return compareTerm(xpr, s, e, state);
207
- }
208
-
209
- function compareTerm(xpr, s, e, state) {
210
- if (Array.isArray(xpr)) {
211
- let i = s;
212
- let not = false;
213
- let between;
214
- while (i < e && xpr[i] !== 'between')
215
- i++;
216
- const b = i < e ? i : -1;
217
- while (i < e && xpr[i] !== 'and')
218
- i++;
219
- const a = i < e ? i : -1;
220
- if (b >= 0) {
221
- const token = [ 'between' ];
222
- not = (xpr[b - 1] === 'not');
223
- if (not)
224
- token.splice(0, 0, 'not');
225
- const expr = expression(xpr, s, not ? b - 1 : b, state);
226
- between = state.array
227
- ? [ expr, ...token ]
228
- : { between: [ expr ] };
229
- if (a >= 0) {
230
- const lower = expression(xpr, b + 1, a, state);
231
- const upper = expression(xpr, a + 1, e, state);
232
- if (state.array)
233
- between.push(lower, 'and', upper);
234
- else
235
- between.between.push(lower, upper);
236
- }
237
- else {
238
- const unspec = expression(xpr, b + 1, e, state);
239
- if (state.array)
240
- between.push(unspec);
241
- else
242
- between.between.push(unspec);
243
- }
244
- if (not && !state.array)
245
- between = { not: between };
246
-
247
- return between;
248
- }
249
- }
250
- return binaryExpr(xpr, (xpr, s, e) => {
251
- const token = [ '=', '<>', '>', '>=', '<', '<=', '==', '!=', 'like', 'in' ];
252
- while (s < e && !token.includes(xpr[s]))
253
- s++;
254
- if (s < e) {
255
- if (xpr[s - 1] === 'not' && (xpr[s] === 'in' || xpr[s] === 'like'))
256
- return [ 2, s - 1 ];
257
- return [ 1, s ];
258
- }
259
- return [ 1, -1 ];
260
- }, expression, s, e, state);
261
- }
262
-
263
- function expression(xpr, s, e, state) {
264
- return binaryExpr(xpr, [ '||' ], exprAddSub, s, e, state);
265
- }
266
-
267
- function exprAddSub(xpr, s, e, state) {
268
- return binaryExpr(xpr, (xpr, s, e) => {
269
- const skips = [ '+', '-', '*', '/' ];
270
- let found = false;
271
- let p = s;
272
- while (!found && p < e) {
273
- found = ((xpr[p] === '+' || xpr[p] === '-') && p > s && !skips.includes(xpr[p - 1]) && p < e);
274
- if (!found)
275
- p++;
276
- }
277
- if (found)
278
- return [ 1, p ];
279
- return [ 1, -1 ];
280
- }, exprMulDiv, s, e, state);
281
- }
282
-
283
- function exprMulDiv(xpr, s, e, state) {
284
- return binaryExpr(xpr, [ '*', '/' ], (state.array ? unary : dot), s, e, state);
285
- }
286
-
287
- function dot(xpr, s, e, state) {
288
- return binaryExpr(xpr, [ '.' ], unary, s, e, state);
289
- }
290
-
291
- function unary(xpr, s, e, state) {
292
- if (Array.isArray(xpr)) {
293
- if (xpr[s] === '+' || xpr[s] === '-' || (!state.array && xpr[s] === 'new')) {
294
- if (state.array)
295
- return [ xpr[s], unary(xpr, s + 1, e, state) ];
296
- return { [xpr[s]]: unary(xpr, s + 1, e, state) };
297
- }
298
- }
299
- return terminal(xpr, s, e, state);
300
- }
301
- function terminal(xpr, s, e, state) {
302
- const csnarray = [
303
- 'ref', 'args', 'columns', 'keys', 'expand', 'inline',
304
- 'requires', 'extensions', 'includes', 'excluding',
305
- ];
306
- const xprarray = [
307
- 'xpr', 'on', 'where', 'orderBy', 'groupBy', 'having',
308
- ];
309
-
310
- if (Array.isArray(xpr) && xpr.length > 0) {
311
- if (e - s <= 1 && state.anno === 0 && typeof xpr[e - 1] !== 'string')
312
- return parseExprInt(xpr[e - 1], state);
313
- return xpr.slice(s, e).map(ix => parseExprInt(ix, state));
314
- }
315
- if (typeof xpr === 'object') {
316
- // if(xpr?.func && funkyfuncs.includes(xpr?.func))
317
- // return xpr;
318
- for (const n in xpr) {
319
- // xpr could be an array with polluted prototype
320
- if (!Object.hasOwnProperty.call(xpr, n))
321
- continue;
322
- const x = xpr[n];
323
- const isAnno = n[0] === '@' && isSimpleAnnoValue(x);
324
- if (isAnno)
325
- state.anno++;
326
- if (Array.isArray(x)) {
327
- if (csnarray.includes(n) || state.anno !== 0)
328
- xpr[n] = x.map(ix => parseExprInt(ix, state));
329
- else if (xprarray.includes(n) && x.length === 1)
330
- xpr[n] = x.map(ix => parseExprInt(ix, state));
331
- else
332
- xpr[n] = parseExprInt(x, state);
333
- }
334
- else {
335
- xpr[n] = parseExprInt(x, state);
336
- }
337
- if (isAnno)
338
- state.anno--;
339
- }
340
- }
341
- return xpr;
342
- }
343
-
344
- function binaryExpr(xpr, token, next, s, e, state) {
345
- const naryExpr = [];
346
- let not = false;
347
- if (Array.isArray(xpr)) {
348
- let [ tl, p ] = findToken(s, e);
349
- if (p >= 0) {
350
- let lhs = next(xpr, s, p, state);
351
- naryExpr.push(lhs);
352
- let op = xpr.slice(p, p + tl);
353
- s = p + tl;
354
- [ tl, p ] = findToken(s, e);
355
- while (p >= 0) {
356
- const rhs = next(xpr, s, p, state);
357
- naryExpr.push(...op, rhs);
358
- if (state.array) {
359
- lhs = [ lhs, ...op, rhs ];
360
- }
361
- else {
362
- not = op.length > 1 && op[0] === 'not';
363
- if (not)
364
- op = op.slice(1);
365
- lhs = (not
366
- ? { not: { [op.join('')]: [ lhs, rhs ] } }
367
- : { [op.join('')]: [ lhs, rhs ] });
368
- }
369
- op = xpr.slice(p, p + tl);
370
- s = p + tl;
371
- [ tl, p ] = findToken(s, e);
372
- }
373
-
374
- let rhs = next(xpr, s, e, state);
375
- if (Array.isArray(rhs) && rhs.length === 0)
376
- rhs = undefined;
377
- naryExpr.push(...op, rhs);
378
-
379
- if (state.array)
380
- return (state.nary ? naryExpr : [ lhs, ...op, rhs ]);
381
-
382
- not = op.length > 1 && op[0] === 'not';
383
- if (not)
384
- op = op.slice(1);
385
- return (not
386
- ? { not: { [op.join('')]: [ lhs, rhs ] } }
387
- : { [op.join('')]: [ lhs, rhs ] });
388
- }
389
- }
390
- return next(xpr, s, e, state);
391
-
392
- function findToken(s, e) {
393
- if (typeof token === 'function')
394
- return token(xpr, s, e);
395
-
396
- while (s < e && !token.includes(xpr[s]))
397
- s++;
398
- if (s < e)
399
- return [ 1, s ];
400
-
401
- return [ 1, -1 ];
402
- }
403
- }
404
- }
405
-
406
- function isSimpleAnnoValue(val) {
407
- // Expressions as annotation values always have a `=` and another property.
408
- // TODO: There must be at least one known expression property, otherwise
409
- // it could be `type: 'unchecked'`.
410
- return !val?.['='] || Object.keys(val) < 2;
411
- }
412
-
413
- module.exports = {
414
- parseExpr,
415
- };