@sap/cds-compiler 3.4.2 → 3.5.0

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 (143) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +1 -0
  3. package/bin/cds_update_identifiers.js +5 -5
  4. package/bin/cdsc.js +15 -16
  5. package/bin/cdshi.js +19 -6
  6. package/doc/CHANGELOG_ARCHIVE.md +2 -2
  7. package/doc/CHANGELOG_BETA.md +9 -1
  8. package/doc/CHANGELOG_DEPRECATED.md +2 -0
  9. package/lib/api/main.js +61 -59
  10. package/lib/api/options.js +4 -2
  11. package/lib/api/validate.js +2 -2
  12. package/lib/base/cleanSymbols.js +2 -3
  13. package/lib/base/dictionaries.js +6 -6
  14. package/lib/base/error.js +2 -2
  15. package/lib/base/keywords.js +6 -6
  16. package/lib/base/location.js +11 -12
  17. package/lib/base/message-registry.js +177 -58
  18. package/lib/base/messages.js +252 -180
  19. package/lib/base/model.js +14 -11
  20. package/lib/base/node-helpers.js +9 -10
  21. package/lib/base/optionProcessorHelper.js +138 -129
  22. package/lib/checks/.eslintrc.json +2 -0
  23. package/lib/checks/actionsFunctions.js +5 -5
  24. package/lib/checks/annotationsOData.js +4 -4
  25. package/lib/checks/arrayOfs.js +1 -1
  26. package/lib/checks/cdsPersistence.js +1 -1
  27. package/lib/checks/checkForTypes.js +3 -3
  28. package/lib/checks/defaultValues.js +3 -3
  29. package/lib/checks/elements.js +7 -7
  30. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  31. package/lib/checks/foreignKeys.js +1 -1
  32. package/lib/checks/invalidTarget.js +4 -4
  33. package/lib/checks/managedInType.js +1 -1
  34. package/lib/checks/managedWithoutKeys.js +1 -1
  35. package/lib/checks/nonexpandableStructured.js +5 -3
  36. package/lib/checks/nullableKeys.js +1 -1
  37. package/lib/checks/onConditions.js +5 -6
  38. package/lib/checks/parameters.js +1 -1
  39. package/lib/checks/queryNoDbArtifacts.js +2 -2
  40. package/lib/checks/selectItems.js +4 -4
  41. package/lib/checks/sql-snippets.js +4 -4
  42. package/lib/checks/types.js +7 -7
  43. package/lib/checks/utils.js +4 -4
  44. package/lib/checks/validator.js +16 -13
  45. package/lib/compiler/.eslintrc.json +4 -1
  46. package/lib/compiler/assert-consistency.js +8 -7
  47. package/lib/compiler/builtins.js +14 -14
  48. package/lib/compiler/checks.js +123 -48
  49. package/lib/compiler/define.js +12 -13
  50. package/lib/compiler/extend.js +266 -60
  51. package/lib/compiler/finalize-parse-cdl.js +10 -5
  52. package/lib/compiler/index.js +17 -14
  53. package/lib/compiler/populate.js +14 -6
  54. package/lib/compiler/propagator.js +2 -0
  55. package/lib/compiler/resolve.js +2 -15
  56. package/lib/compiler/shared.js +27 -16
  57. package/lib/compiler/tweak-assocs.js +5 -6
  58. package/lib/compiler/utils.js +20 -0
  59. package/lib/edm/annotations/genericTranslation.js +604 -358
  60. package/lib/edm/annotations/preprocessAnnotations.js +39 -35
  61. package/lib/edm/csn2edm.js +275 -222
  62. package/lib/edm/edm.js +17 -3
  63. package/lib/edm/edmAnnoPreprocessor.js +6 -6
  64. package/lib/edm/edmInboundChecks.js +2 -2
  65. package/lib/edm/edmPreprocessor.js +107 -77
  66. package/lib/edm/edmUtils.js +44 -5
  67. package/lib/gen/Dictionary.json +210 -8
  68. package/lib/gen/language.checksum +1 -1
  69. package/lib/gen/language.interp +67 -63
  70. package/lib/gen/language.tokens +81 -81
  71. package/lib/gen/languageLexer.interp +4 -10
  72. package/lib/gen/languageLexer.js +854 -869
  73. package/lib/gen/languageLexer.tokens +79 -81
  74. package/lib/gen/languageParser.js +14309 -13832
  75. package/lib/inspect/inspectModelStatistics.js +2 -2
  76. package/lib/inspect/inspectPropagation.js +6 -6
  77. package/lib/inspect/inspectUtils.js +2 -2
  78. package/lib/json/from-csn.js +102 -55
  79. package/lib/json/to-csn.js +119 -198
  80. package/lib/language/antlrParser.js +5 -2
  81. package/lib/language/docCommentParser.js +6 -6
  82. package/lib/language/errorStrategy.js +43 -23
  83. package/lib/language/genericAntlrParser.js +113 -133
  84. package/lib/language/language.g4 +1550 -1506
  85. package/lib/language/multiLineStringParser.js +3 -3
  86. package/lib/language/textUtils.js +2 -2
  87. package/lib/main.js +3 -3
  88. package/lib/model/csnRefs.js +5 -0
  89. package/lib/model/csnUtils.js +130 -122
  90. package/lib/model/revealInternalProperties.js +1 -1
  91. package/lib/model/sortViews.js +4 -6
  92. package/lib/modelCompare/compare.js +2 -2
  93. package/lib/modelCompare/utils/.eslintrc.json +22 -0
  94. package/lib/modelCompare/utils/filter.js +100 -0
  95. package/lib/optionProcessor.js +5 -0
  96. package/lib/render/.eslintrc.json +1 -0
  97. package/lib/render/DuplicateChecker.js +1 -1
  98. package/lib/render/manageConstraints.js +12 -12
  99. package/lib/render/toCdl.js +311 -276
  100. package/lib/render/toHdbcds.js +97 -94
  101. package/lib/render/toRename.js +5 -5
  102. package/lib/render/toSql.js +127 -223
  103. package/lib/render/utils/common.js +141 -108
  104. package/lib/render/utils/delta.js +227 -0
  105. package/lib/render/utils/sql.js +22 -6
  106. package/lib/render/utils/stringEscapes.js +3 -3
  107. package/lib/transform/db/.eslintrc.json +2 -0
  108. package/lib/transform/db/applyTransformations.js +3 -3
  109. package/lib/transform/db/assertUnique.js +13 -12
  110. package/lib/transform/db/associations.js +5 -5
  111. package/lib/transform/db/cdsPersistence.js +10 -8
  112. package/lib/transform/db/constraints.js +14 -14
  113. package/lib/transform/db/expansion.js +20 -22
  114. package/lib/transform/db/flattening.js +24 -42
  115. package/lib/transform/db/groupByOrderBy.js +3 -3
  116. package/lib/transform/db/temporal.js +6 -6
  117. package/lib/transform/db/transformExists.js +23 -23
  118. package/lib/transform/db/views.js +16 -16
  119. package/lib/transform/draft/.eslintrc.json +1 -35
  120. package/lib/transform/draft/db.js +10 -10
  121. package/lib/transform/draft/odata.js +2 -2
  122. package/lib/transform/forOdataNew.js +8 -29
  123. package/lib/transform/forRelationalDB.js +16 -6
  124. package/lib/transform/localized.js +11 -10
  125. package/lib/transform/odata/toFinalBaseType.js +41 -27
  126. package/lib/transform/odata/typesExposure.js +113 -47
  127. package/lib/transform/parseExpr.js +209 -106
  128. package/lib/transform/transformUtilsNew.js +17 -10
  129. package/lib/transform/translateAssocsToJoins.js +24 -19
  130. package/lib/transform/universalCsn/coreComputed.js +10 -10
  131. package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
  132. package/lib/transform/universalCsn/utils.js +3 -3
  133. package/lib/utils/file.js +5 -5
  134. package/lib/utils/moduleResolve.js +13 -13
  135. package/lib/utils/objectUtils.js +6 -6
  136. package/lib/utils/term.js +5 -2
  137. package/lib/utils/timetrace.js +51 -24
  138. package/package.json +5 -8
  139. package/share/messages/check-proper-type-of.md +1 -1
  140. package/share/messages/message-explanations.json +1 -1
  141. package/share/messages/redirected-to-complex.md +4 -4
  142. package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
  143. package/lib/modelCompare/filter.js +0 -83
@@ -1,8 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const keywords = require('../base/keywords');
4
- const { isBuiltinType, generatedByCompilerVersion, getNormalizedQuery } = require('../model/csnUtils');
5
- const { findElement, getExpressionRenderer } = require('./utils/common');
4
+ const { findElement, createExpressionRenderer, withoutCast } = require('./utils/common');
6
5
  const { escapeString, hasUnpairedUnicodeSurrogate } = require('./utils/stringEscapes');
7
6
  const { checkCSNVersion } = require('../json/csnVersion');
8
7
  const { timetrace } = require('../utils/timetrace');
@@ -12,6 +11,12 @@ const { isBetaEnabled } = require('../base/model');
12
11
  const { ModelError } = require('../base/error');
13
12
  const { typeParameters, specialFunctions } = require('../compiler/builtins');
14
13
  const { forEach } = require('../utils/objectUtils');
14
+ const {
15
+ isBuiltinType,
16
+ generatedByCompilerVersion,
17
+ getNormalizedQuery,
18
+ cloneCsnNonDict,
19
+ } = require('../model/csnUtils');
15
20
 
16
21
  const identifierRegex = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
17
22
 
@@ -24,15 +29,36 @@ const identifierRegex = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
24
29
  * @param {CSN.Model} csn
25
30
  * @param {CSN.Options} [options]
26
31
  */
27
- function csnToCdl(csn, options) {
32
+ function csnToCdl( csn, options ) {
28
33
  timetrace.start('CDL rendering');
29
- let _renderExpr = null;
30
34
 
31
- if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
35
+ if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn')) {
36
+ // Since the expander modifies the CSN, we need to clone it first or
37
+ // toCdl can't guarantee that the input CSN is not modified.
38
+ csn = cloneCsnNonDict(csn, options);
32
39
  enrichUniversalCsn(csn, options);
40
+ }
33
41
 
34
42
  checkCSNVersion(csn, options);
35
43
 
44
+ const exprRenderer = createCdlExpressionRenderer();
45
+ const usings = {
46
+ list: [],
47
+ available: availableFirstPathSteps(csn),
48
+ addIfRequired(name) {
49
+ // RegEx is at least twice as fast as .split()[0]
50
+ const first = name.match(/^[^.]+/)[0];
51
+ if (!this.available.includes(first) && !this.list.includes(first))
52
+ this.list.push(first);
53
+ },
54
+ renderUsings() {
55
+ if (this.list.length === 0)
56
+ return '';
57
+ return `using { ${this.list.join(', ')} };\n\n`;
58
+ },
59
+ };
60
+ const hanaRequiresAbsolutePath = usings.available.includes('hana');
61
+
36
62
  const cdlResult = Object.create(null);
37
63
  cdlResult.model = options.testMode ? '' : `// ${generatedByCompilerVersion()} \n`;
38
64
 
@@ -52,7 +78,14 @@ function csnToCdl(csn, options) {
52
78
  cdlResult.namespace += 'using from \'./model.cds\';';
53
79
  }
54
80
 
55
- timetrace.stop();
81
+ cdlResult.model = usings.renderUsings() + cdlResult.model;
82
+ if (csn.requires) {
83
+ let usingsStr = csn.requires.map(req => `using from '${req}';`).join('\n');
84
+ usingsStr += '\n\n';
85
+ cdlResult.model = usingsStr + cdlResult.model;
86
+ }
87
+
88
+ timetrace.stop('CDL rendering');
56
89
  return cdlResult;
57
90
 
58
91
  /**
@@ -79,12 +112,12 @@ function csnToCdl(csn, options) {
79
112
  * @param {object} vocabularies
80
113
  * @return {string}
81
114
  */
82
- function renderVocabularies(vocabularies) {
115
+ function renderVocabularies( vocabularies ) {
83
116
  let result = '';
84
117
  forEach(vocabularies, renderVocabulariesEntry);
85
118
  return result;
86
119
 
87
- function renderVocabulariesEntry(name, anno) {
120
+ function renderVocabulariesEntry( name, anno ) {
88
121
  if (!anno._ignore) {
89
122
  // This environment is passed down the call hierarchy, for dealing with
90
123
  // indentation and name resolution issues
@@ -104,7 +137,7 @@ function csnToCdl(csn, options) {
104
137
  * @param {CdlRenderEnvironment} env
105
138
  * @return {string}
106
139
  */
107
- function renderExtensions(extensions, env) {
140
+ function renderExtensions( extensions, env ) {
108
141
  if (!env.path)
109
142
  env = envNewPath(env, [ 'extensions' ]);
110
143
  return extensions.map((ext, index) => renderExtension(ext, envAddPath(env, [ index ]))).join('\n');
@@ -117,7 +150,7 @@ function csnToCdl(csn, options) {
117
150
  * @param {CdlRenderEnvironment} env
118
151
  * @return {string}
119
152
  */
120
- function renderExtension(ext, env) {
153
+ function renderExtension( ext, env ) {
121
154
  if (ext.extend)
122
155
  return renderExtendStatement(ext.extend, ext, env);
123
156
  return renderAnnotateStatement(ext, env);
@@ -133,20 +166,33 @@ function csnToCdl(csn, options) {
133
166
  * @param {CdlRenderEnvironment} env
134
167
  * @return {string}
135
168
  */
136
- function renderExtendStatement(extName, ext, env) {
137
- extName = renderArtifactName(extName);
138
- // Element extensions have `kind` set.
139
- const isElementExtend = (ext.kind === 'extend');
169
+ function renderExtendStatement( extName, ext, env ) {
170
+ // Element extensions have `kind` set. Don't use for enum extension.
171
+ const isElementExtend = (ext.kind === 'extend' && !ext.enum);
140
172
  let result = renderAnnotationAssignmentsAndDocComment(ext, env);
173
+ extName = isElementExtend ? renderArtifactName(extName) : renderDefinitionReference(extName);
141
174
 
142
175
  if (ext.includes && ext.includes.length > 0) {
143
176
  // Includes can't be combined with anything in braces {}.
144
177
  const affix = isElementExtend ? 'element ' : '';
145
- const includes = ext.includes.map(inc => quotePathIfRequired(inc)).join(', ');
178
+ const includes = ext.includes.map(inc => renderDefinitionReference(inc)).join(', ');
146
179
  result += `${env.indent}extend ${affix}${extName} with ${includes};\n`;
147
180
  return result;
148
181
  }
149
182
 
183
+ const typeParams = renderTypeParameters(ext, true);
184
+ if (typeParams) {
185
+ result += `${env.indent}extend ${extName} with ${typeParams};\n`;
186
+ return result;
187
+ }
188
+
189
+ // If there is nothing to extend, e.g. only annotations, don't render an
190
+ // empty element list. This would end up in diffs with parseCdl CSN.
191
+ if (!ext.elements && !ext.columns && !ext.actions && !ext.enum) {
192
+ result += `${env.indent}extend ${extName};\n`;
193
+ return result;
194
+ }
195
+
150
196
  // We have the "old-style" prefix syntax and the "new-style" postfix "with <type>" syntax.
151
197
  // The former one can not only extend (sub-)elements but also actions in the same statement whereas
152
198
  // the latter cannot.
@@ -157,18 +203,12 @@ function csnToCdl(csn, options) {
157
203
  else
158
204
  result += `${env.indent}extend ${extName} with ${getExtendPostfixVariant(ext)} {\n`;
159
205
 
160
- if (ext.columns) {
206
+ if (ext.columns)
161
207
  result += renderViewColumns(ext.columns, increaseIndent(env));
162
- }
163
- else if (ext.elements) {
208
+
209
+ else if (ext.elements || ext.enum)
164
210
  result += renderExtendStatementElements(ext, env);
165
- }
166
- else if (ext.enum) {
167
- const childEnv = increaseIndent(env);
168
- forEach(ext.enum, (name, value) => {
169
- result += renderEnumElement(name, value, childEnv);
170
- });
171
- }
211
+
172
212
 
173
213
  // Not part of if/else cascade, because it may be in postfix notation.
174
214
  if (ext.actions) {
@@ -194,7 +234,7 @@ function csnToCdl(csn, options) {
194
234
  * @param {object} ext
195
235
  * @return {string}
196
236
  */
197
- function getExtendPrefixVariant(ext) {
237
+ function getExtendPrefixVariant( ext ) {
198
238
  if (ext.kind === 'extend')
199
239
  return 'element'; // element extensions inside an `extend`
200
240
  if (ext.columns)
@@ -210,7 +250,7 @@ function csnToCdl(csn, options) {
210
250
  * @param {CSN.Extension} ext
211
251
  * @return {string}
212
252
  */
213
- function getExtendPostfixVariant(ext) {
253
+ function getExtendPostfixVariant( ext ) {
214
254
  if (ext.columns)
215
255
  return 'columns';
216
256
  if (ext.actions)
@@ -229,15 +269,15 @@ function csnToCdl(csn, options) {
229
269
  * @param {CdlRenderEnvironment} env
230
270
  * @return {string}
231
271
  */
232
- function renderExtendStatementElements(ext, env) {
272
+ function renderExtendStatementElements( ext, env ) {
233
273
  let result = '';
234
- forEach(ext.elements || {}, (elemName, element) => {
274
+ forEach(ext.elements || ext.enum || {}, (elemName, element) => {
235
275
  if (element.kind === 'extend')
236
276
  result += renderExtendStatement(elemName, element, increaseIndent(env));
237
277
  else
238
278
  // As soon as we are inside an element, nested `extend` are not possible,
239
279
  // since we can't extend an existing element of a new one.
240
- result += renderElement(elemName, element, increaseIndent(env), true);
280
+ result += renderElement(elemName, element, increaseIndent(env));
241
281
  });
242
282
  return result;
243
283
  }
@@ -249,9 +289,11 @@ function csnToCdl(csn, options) {
249
289
  * @param {CdlRenderEnvironment} env
250
290
  * @return {string}
251
291
  */
252
- function renderAnnotateStatement(ext, env) {
292
+ function renderAnnotateStatement( ext, env ) {
253
293
  // Top-level annotations of the artifact
254
294
  let result = renderAnnotationAssignmentsAndDocComment(ext, env);
295
+ // Note: Not renderDefinitionReference, because we don't care if there
296
+ // are annotations to unknown things. That's allowed!
255
297
  result += `${env.indent}annotate ${renderArtifactName(ext.annotate)}`;
256
298
 
257
299
  if (ext.params)
@@ -260,6 +302,8 @@ function csnToCdl(csn, options) {
260
302
  // Element extensions and annotations (possibly nested)
261
303
  if (ext.elements)
262
304
  result += renderAnnotateStatementElements(ext.elements, env);
305
+ if (ext.enum)
306
+ result += renderAnnotateStatementElements(ext.enum, env);
263
307
 
264
308
  // Returns annotations
265
309
  if (ext.returns) {
@@ -298,18 +342,21 @@ function csnToCdl(csn, options) {
298
342
  * 'elements' (assuming that the surrounding parent has just been rendered, without trailing newline).
299
343
  * Returns the resulting source string, ending without a trailing newline.
300
344
  *
301
- * @param {CSN.Elements} elements
345
+ * @param {object} elements
302
346
  * @param {CdlRenderEnvironment} env
303
347
  * @return {string}
304
348
  */
305
- function renderAnnotateStatementElements(elements, env) {
349
+ function renderAnnotateStatementElements( elements, env ) {
306
350
  let result = ' {\n';
307
351
  const childEnv = increaseIndent(env);
308
352
  for (const name in elements) {
309
353
  const elem = elements[name];
310
- result += renderAnnotationAssignmentsAndDocComment(elem, childEnv) + childEnv.indent + quoteIdIfRequired(name);
354
+ result += renderAnnotationAssignmentsAndDocComment(elem, childEnv);
355
+ result += childEnv.indent + quoteIdIfRequired(name);
311
356
  if (elem.elements)
312
357
  result += renderAnnotateStatementElements(elem.elements, childEnv);
358
+ if (elem.enum)
359
+ result += renderAnnotateStatementElements(elem.enum, childEnv);
313
360
 
314
361
  result += ';\n';
315
362
  }
@@ -324,7 +371,7 @@ function csnToCdl(csn, options) {
324
371
  * @param {CdlRenderEnvironment} env
325
372
  * @return {string}
326
373
  */
327
- function renderAnnotateParamsInParentheses(params, env) {
374
+ function renderAnnotateParamsInParentheses( params, env ) {
328
375
  const childEnv = increaseIndent(env);
329
376
  let result = '(\n';
330
377
  const paramAnnotations = [];
@@ -342,7 +389,7 @@ function csnToCdl(csn, options) {
342
389
  * @param {CSN.Artifact} art
343
390
  * @param {CdlRenderEnvironment} env
344
391
  */
345
- function renderArtifact(artifactName, art, env) {
392
+ function renderArtifact( artifactName, art, env ) {
346
393
  env = envNewPath(env, [ 'definitions', artifactName ]);
347
394
  env.artifactName = artifactName;
348
395
 
@@ -379,7 +426,7 @@ function csnToCdl(csn, options) {
379
426
  * @param {CSN.Artifact} art
380
427
  * @param {CdlRenderEnvironment} env
381
428
  */
382
- function renderEvent(artifactName, art, env) {
429
+ function renderEvent( artifactName, art, env ) {
383
430
  let result = renderAnnotationAssignmentsAndDocComment(art, env);
384
431
  const normalizedArtifactName = renderArtifactName(artifactName);
385
432
  result += `${env.indent}event ${normalizedArtifactName}`;
@@ -407,7 +454,7 @@ function csnToCdl(csn, options) {
407
454
  * @param {CdlRenderEnvironment} env
408
455
  * @returns {string}
409
456
  */
410
- function renderContextOrService(artifactName, art, env) {
457
+ function renderContextOrService( artifactName, art, env ) {
411
458
  let result = renderAnnotationAssignmentsAndDocComment(art, env);
412
459
  result += `${env.indent}${art.kind} ${renderArtifactName(artifactName)}`;
413
460
  return `${result} {};\n`;
@@ -421,7 +468,7 @@ function csnToCdl(csn, options) {
421
468
  * @param {CdlRenderEnvironment} env
422
469
  * @return {string}
423
470
  */
424
- function renderEntity(artifactName, art, env) {
471
+ function renderEntity( artifactName, art, env ) {
425
472
  let result = renderAnnotationAssignmentsAndDocComment(art, env);
426
473
  result += env.indent + (art.abstract ? 'abstract ' : '');
427
474
  result += `entity ${renderArtifactName(artifactName)}`;
@@ -445,7 +492,7 @@ function csnToCdl(csn, options) {
445
492
  * @param {CdlRenderEnvironment} env
446
493
  * @return {string}
447
494
  */
448
- function renderAspect(artifactName, art, env) {
495
+ function renderAspect( artifactName, art, env ) {
449
496
  let result = renderAnnotationAssignmentsAndDocComment(art, env);
450
497
  result += `${env.indent}aspect ${renderArtifactName(artifactName)}`;
451
498
  if (art.includes)
@@ -468,11 +515,11 @@ function csnToCdl(csn, options) {
468
515
  * @param {CdlRenderEnvironment} env
469
516
  * @return {string}
470
517
  */
471
- function renderElements(artifact, env) {
518
+ function renderElements( artifact, env ) {
472
519
  let elements = '';
473
520
  const childEnv = increaseIndent(env);
474
521
  for (const name in artifact.elements)
475
- elements += renderElement(name, artifact.elements[name], childEnv, null);
522
+ elements += renderElement(name, artifact.elements[name], childEnv);
476
523
 
477
524
  if (elements === '')
478
525
  return '{ }';
@@ -480,23 +527,34 @@ function csnToCdl(csn, options) {
480
527
  }
481
528
 
482
529
  /**
483
- * Render an element (of an entity, type or annotation, not a projection or view).
484
- * Return the resulting source string.
530
+ * Render an element (of an entity, type or annotation, not a projection or view)
531
+ * or an enum symbol.
532
+ * Returns the resulting source string.
485
533
  *
486
534
  * @param {string} elementName
487
- * @param {CSN.Element} elm
535
+ * @param {CSN.Element|CSN.Enum} elm
488
536
  * @param {CdlRenderEnvironment} env
489
- * @param {Boolean} [isSubElement]
490
537
  */
491
- function renderElement(elementName, elm, env, isSubElement) {
538
+ function renderElement( elementName, elm, env ) {
492
539
  env = envAddPath(env, [ 'elements', elementName ]);
493
540
  let result = renderAnnotationAssignmentsAndDocComment(elm, env);
494
541
  result += env.indent;
495
542
  result += elm.virtual ? 'virtual ' : '';
496
- result += elm.key && !isSubElement ? 'key ' : '';
543
+ result += elm.key ? 'key ' : '';
497
544
  // TODO(v4): Remove once deprecated flag for `masked` is removed.
498
545
  result += elm.masked ? 'masked ' : '';
499
- result += `${quoteIdIfRequired(elementName)} : ${renderTypeReferenceAndProps(elm, env)}`;
546
+ result += quoteIdIfRequired(elementName);
547
+ if (elm.val !== undefined) {
548
+ result += ` = ${exprRenderer.renderExpr(elm, env)}`;
549
+ }
550
+ else if (elm['#'] !== undefined) {
551
+ result += ` = #${elm['#']}`;
552
+ }
553
+ else {
554
+ const props = renderTypeReferenceAndProps(elm, env);
555
+ if (props !== '')
556
+ result += ` : ${props}`;
557
+ }
500
558
 
501
559
  return `${result};\n`;
502
560
  }
@@ -516,8 +574,8 @@ function csnToCdl(csn, options) {
516
574
  * @param {CdlRenderEnvironment} env
517
575
  * @return {string}
518
576
  */
519
- function renderQueryElementAndEnumAnnotations(artifactName, art, env) {
520
- const annotate = collectAnnotationsOfElementsAndEnum(art, { artifactName, path: env.path });
577
+ function renderQueryElementAndEnumAnnotations( artifactName, art, env ) {
578
+ const annotate = collectAnnotationsOfElementsAndEnum(art, createEnv({ artifactName, path: env.path }));
521
579
  if (annotate)
522
580
  return renderExtensions([ annotate ], env);
523
581
  return '';
@@ -531,7 +589,7 @@ function csnToCdl(csn, options) {
531
589
  * @param {CdlRenderEnvironment} env
532
590
  * @return {CSN.Extension|null}
533
591
  */
534
- function collectAnnotationsOfElementsAndEnum(artifact, env) {
592
+ function collectAnnotationsOfElementsAndEnum( artifact, env ) {
535
593
  // Array, which may be annotated as well.
536
594
  if (artifact.items) {
537
595
  env = envAddPath(env, [ 'items' ]);
@@ -573,7 +631,7 @@ function csnToCdl(csn, options) {
573
631
  *
574
632
  * @return {boolean} True, if there were annotations, false otherwise.
575
633
  */
576
- function collectAnnos(annotateObj, art) {
634
+ function collectAnnos( annotateObj, art ) {
577
635
  if (!art.elements && !art.enum)
578
636
  return false;
579
637
 
@@ -617,7 +675,7 @@ function csnToCdl(csn, options) {
617
675
  * @param {CdlRenderEnvironment} env
618
676
  * @return {string}
619
677
  */
620
- function renderViewSource(source, env) {
678
+ function renderViewSource( source, env ) {
621
679
  // Sub-SELECT
622
680
  if (source.SELECT || source.SET) {
623
681
  let result = `(${renderQuery(source, false, 'view', increaseIndent(env))})`;
@@ -635,7 +693,7 @@ function csnToCdl(csn, options) {
635
693
  result += renderJoinCardinality(source.cardinality);
636
694
  result += `join ${renderViewSource(source.args[i], env)}`;
637
695
  if (source.on)
638
- result += ` on ${renderExpr(source.on, env, true, true)}`;
696
+ result += ` on ${exprRenderer.renderExpr(source.on, env)}`;
639
697
  }
640
698
  result += ')';
641
699
  return result;
@@ -645,7 +703,7 @@ function csnToCdl(csn, options) {
645
703
  return renderAbsolutePathWithAlias(source, env);
646
704
  }
647
705
 
648
- function renderJoinCardinality(card) {
706
+ function renderJoinCardinality( card ) {
649
707
  let result = '';
650
708
  if (card) {
651
709
  if (card.srcmin && card.srcmin === 1)
@@ -669,17 +727,16 @@ function csnToCdl(csn, options) {
669
727
  * @param {CdlRenderEnvironment} env
670
728
  * @return {string}
671
729
  */
672
- function renderAbsolutePath(path, env) {
730
+ function renderAbsolutePath( path, env ) {
673
731
  // Sanity checks
674
732
  if (!path.ref)
675
733
  throw new ModelError(`Expecting ref in path: ${JSON.stringify(path)}`);
676
734
 
677
-
678
735
  // Determine the absolute name of the first artifact on the path (before any associations or element traversals)
679
736
  const firstArtifactName = path.ref[0].id || path.ref[0];
680
737
 
681
738
  // Render the first path step (absolute name, with different quoting/naming ..)
682
- let result = renderArtifactName(firstArtifactName);
739
+ let result = renderDefinitionReference(firstArtifactName);
683
740
 
684
741
  // Even the first step might have parameters and/or a filter
685
742
  if (path.ref[0].args)
@@ -687,13 +744,13 @@ function csnToCdl(csn, options) {
687
744
 
688
745
  if (path.ref[0].where) {
689
746
  const cardinality = path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : '';
690
- const expr = renderExpr(path.ref[0].where, env, true, true);
747
+ const expr = exprRenderer.renderExpr(path.ref[0].where, env);
691
748
  result += `[${cardinality}${expr}]`;
692
749
  }
693
750
 
694
751
  // Add any path steps (possibly with parameters and filters) that may follow after that
695
752
  if (path.ref.length > 1)
696
- result += `:${renderExpr({ ref: path.ref.slice(1) }, env)}`;
753
+ result += `:${exprRenderer.renderExpr({ ref: path.ref.slice(1) }, env)}`;
697
754
 
698
755
  return result;
699
756
  }
@@ -709,7 +766,7 @@ function csnToCdl(csn, options) {
709
766
  * @param {CdlRenderEnvironment} env
710
767
  * @return {string}
711
768
  */
712
- function renderAbsolutePathWithAlias(path, env) {
769
+ function renderAbsolutePathWithAlias( path, env ) {
713
770
  let result = renderAbsolutePath(path, env);
714
771
  if (path.as) {
715
772
  // Source had an alias - render it
@@ -726,7 +783,7 @@ function csnToCdl(csn, options) {
726
783
  * @param {CdlRenderEnvironment} env
727
784
  * @return {string}
728
785
  */
729
- function renderViewColumns(columns, env, elements = Object.create(null)) {
786
+ function renderViewColumns( columns, env, elements = Object.create(null) ) {
730
787
  const result = columns.map(col => renderViewColumn(col, env, findElement(elements, col)))
731
788
  .filter(s => s !== '')
732
789
  .join(',\n');
@@ -742,7 +799,7 @@ function csnToCdl(csn, options) {
742
799
  * @param {CdlRenderEnvironment} env
743
800
  * @param {CSN.Element} element Element corresponding to the column. Generated by the compiler.
744
801
  */
745
- function renderViewColumn(col, env, element) {
802
+ function renderViewColumn( col, env, element ) {
746
803
  // Annotations and column
747
804
  let result = '';
748
805
  if (!col.doc) {
@@ -755,17 +812,19 @@ function csnToCdl(csn, options) {
755
812
  result += env.indent;
756
813
 
757
814
  // only if column is virtual, keyword virtual was present in the source text
758
- if (col.virtual)
759
- result += 'virtual ';
815
+ result += col.virtual ? 'virtual ' : '';
816
+ result += col.key ? 'key ' : '';
760
817
 
761
- const key = col.key ? 'key ' : '';
762
818
  // Use special rendering for .expand/.inline - renderExpr cannot easily handle some cases
763
- result += key + ((col.expand || col.inline) ? renderInlineExpand(col, env) : renderExpr(col, env, true));
819
+ if (col.expand || col.inline)
820
+ result += renderInlineExpand(col, env);
821
+ else
822
+ result += exprRenderer.renderExpr(withoutCast(col), env);
764
823
 
765
824
  // Alias for inline/expand is already handled by renderInlineExpand
766
825
  // A new association (cast with `type` and `target`) uses `as` as its primary name, not alias.
767
- const isNewAssociation = col.cast && col.cast.type && col.cast.target;
768
- if (col.as && !col.inline && !col.expand && !isNewAssociation)
826
+ const isNewAssociation = col.cast?.type && col.cast.target;
827
+ if (!isNewAssociation && col.as && !col.inline && !col.expand)
769
828
  result += ` as ${quoteIdIfRequired(col.as)}`;
770
829
 
771
830
  // Explicit type provided for the view element?
@@ -787,9 +846,9 @@ function csnToCdl(csn, options) {
787
846
  * @param {CdlRenderEnvironment} env
788
847
  * @returns {string}
789
848
  */
790
- function renderInlineExpand(obj, env) {
849
+ function renderInlineExpand( obj, env ) {
791
850
  // No expression to render for { * } as alias
792
- let result = (obj.as && obj.expand && !obj.ref) ? '' : renderExpr(obj, env);
851
+ let result = (obj.as && obj.expand && !obj.ref) ? '' : exprRenderer.renderExpr(withoutCast(obj), env);
793
852
 
794
853
  // s as alias { * }
795
854
  if (obj.as && (obj.ref || obj.xpr || obj.val !== undefined || obj.func !== undefined))
@@ -812,11 +871,9 @@ function csnToCdl(csn, options) {
812
871
  // Drill down and render children of the expand/inline
813
872
  const childEnv = increaseIndent(env);
814
873
  const expandInline = obj.expand || obj.inline;
815
- expandInline.forEach((elm, i) => {
816
- result += `${childEnv.indent}${renderInlineExpand(elm, childEnv)}`;
817
- if (i < expandInline.length - 1)
818
- result += ',\n';
819
- });
874
+ result += expandInline //
875
+ .map(elm => renderAnnotationAssignmentsAndDocComment(elm, childEnv) + childEnv.indent + renderInlineExpand(elm, childEnv))
876
+ .join(',\n');
820
877
  result += `\n${env.indent}}`;
821
878
 
822
879
  // Don't forget about the .excluding
@@ -837,7 +894,7 @@ function csnToCdl(csn, options) {
837
894
  * @param {CdlRenderEnvironment} env
838
895
  * @returns {String}
839
896
  */
840
- function renderDocComment(obj, env) {
897
+ function renderDocComment( obj, env ) {
841
898
  if (!obj || obj && obj.doc === undefined)
842
899
  return '';
843
900
  else if (obj && obj.doc === null) // empty doc comment needs to be rendered
@@ -853,15 +910,11 @@ function csnToCdl(csn, options) {
853
910
  }
854
911
 
855
912
  /**
856
- * Render a view. If '$syntax' is set (to 'projection', 'view', 'entity'),
857
- * the view query is rendered in the requested syntax style, otherwise it
858
- * is rendered as a view.
859
- *
860
913
  * @param {string} artifactName
861
914
  * @param {CSN.Artifact} art
862
915
  * @param {CdlRenderEnvironment} env
863
916
  */
864
- function renderView(artifactName, art, env) {
917
+ function renderView( artifactName, art, env ) {
865
918
  const syntax = (art.projection) ? 'projection' : 'entity';
866
919
  let result = renderAnnotationAssignmentsAndDocComment(art, env);
867
920
  result += `${env.indent}entity ${renderArtifactName(artifactName)}`;
@@ -891,7 +944,7 @@ function csnToCdl(csn, options) {
891
944
  * @param {CSN.Path} [path=[]]
892
945
  * @param {object} [elements]
893
946
  */
894
- function renderQuery(query, isLeadingQuery, syntax, env, path = [], elements = query.elements || Object.create(null)) {
947
+ function renderQuery( query, isLeadingQuery, syntax, env, path = [], elements = query.elements || Object.create(null) ) {
895
948
  if (query.SET) {
896
949
  // Set operator, such as UNION, INTERSECT, or EXCEPT...
897
950
  return renderQuerySet();
@@ -937,13 +990,13 @@ function csnToCdl(csn, options) {
937
990
  result += renderActionsAndFunctions(query, env);
938
991
 
939
992
  if (select.where)
940
- result += `${continueIndent(result, env)}where ${renderExpr(select.where, env, true, true)}`;
993
+ result += `${continueIndent(result, env)}where ${exprRenderer.renderExpr(select.where, env)}`;
941
994
 
942
995
  if (select.groupBy)
943
- result += `${continueIndent(result, env)}group by ${select.groupBy.map(expr => renderExpr(expr, env, true, false, true)).join(', ')}`;
996
+ result += `${continueIndent(result, env)}group by ${select.groupBy.map(expr => exprRenderer.renderExpr(expr, env)).join(', ')}`;
944
997
 
945
998
  if (select.having)
946
- result += `${continueIndent(result, env)}having ${renderExpr(select.having, env, true, true)}`;
999
+ result += `${continueIndent(result, env)}having ${exprRenderer.renderExpr(select.having, env)}`;
947
1000
 
948
1001
  if (select.orderBy)
949
1002
  result += `${continueIndent(result, env)}order by ${select.orderBy.map(entry => renderOrderByEntry(entry, env)).join(', ')}`;
@@ -960,7 +1013,7 @@ function csnToCdl(csn, options) {
960
1013
  * @param {CdlRenderEnvironment} indentEnv
961
1014
  * @return {string}
962
1015
  */
963
- function continueIndent(str, indentEnv) {
1016
+ function continueIndent( str, indentEnv ) {
964
1017
  if (str.endsWith('}') || str.endsWith('})')) {
965
1018
  // The preceding clause ended with '}', just append after that
966
1019
  return ' ';
@@ -976,14 +1029,14 @@ function csnToCdl(csn, options) {
976
1029
  * @param {CdlRenderEnvironment} limitEnv
977
1030
  * @return {string}
978
1031
  */
979
- function renderLimit(limit, limitEnv) {
1032
+ function renderLimit( limit, limitEnv ) {
980
1033
  let limitStr = '';
981
1034
  if (limit.rows !== undefined)
982
- limitStr += `limit ${renderExpr(limit.rows, limitEnv)}`;
1035
+ limitStr += `limit ${exprRenderer.renderExpr(limit.rows, limitEnv)}`;
983
1036
 
984
1037
  if (limit.offset !== undefined) {
985
1038
  const offsetIndent = (limitStr === '') ? '' : `\n${increaseIndent(limitEnv).indent}`;
986
- limitStr += `${offsetIndent}offset ${renderExpr(limit.offset, limitEnv)}`;
1039
+ limitStr += `${offsetIndent}offset ${exprRenderer.renderExpr(limit.offset, limitEnv)}`;
987
1040
  }
988
1041
  return limitStr;
989
1042
  }
@@ -1020,8 +1073,8 @@ function csnToCdl(csn, options) {
1020
1073
  * @param {CdlRenderEnvironment} env
1021
1074
  * @return {string}
1022
1075
  */
1023
- function renderOrderByEntry(entry, env) {
1024
- let result = renderAnnotationAssignmentsAndDocComment(entry, env) + renderExpr(entry, env, true, false, true);
1076
+ function renderOrderByEntry( entry, env ) {
1077
+ let result = renderAnnotationAssignmentsAndDocComment(entry, env) + exprRenderer.renderExpr(entry, env);
1025
1078
  if (entry.sort)
1026
1079
  result += ` ${entry.sort}`;
1027
1080
 
@@ -1041,7 +1094,7 @@ function csnToCdl(csn, options) {
1041
1094
  * @param {CdlRenderEnvironment} env
1042
1095
  * @return {string}
1043
1096
  */
1044
- function renderActionsAndFunctions(art, env) {
1097
+ function renderActionsAndFunctions( art, env ) {
1045
1098
  let result = '';
1046
1099
  const childEnv = increaseIndent(env);
1047
1100
  for (const name in art.actions)
@@ -1062,7 +1115,7 @@ function csnToCdl(csn, options) {
1062
1115
  * @param {CdlRenderEnvironment} env
1063
1116
  * @return {string}
1064
1117
  */
1065
- function renderActionOrFunction(actionName, act, env) {
1118
+ function renderActionOrFunction( actionName, act, env ) {
1066
1119
  let result = renderAnnotationAssignmentsAndDocComment(act, env) + env.indent + act.kind;
1067
1120
  result += ` ${renderArtifactName(actionName)}`;
1068
1121
  result += renderParameters(act, env);
@@ -1084,7 +1137,7 @@ function csnToCdl(csn, options) {
1084
1137
  * @param {CdlRenderEnvironment} env
1085
1138
  * @returns {string}
1086
1139
  */
1087
- function renderParameters(art, env) {
1140
+ function renderParameters( art, env ) {
1088
1141
  const childEnv = increaseIndent(env);
1089
1142
  const parameters = Object.keys(art.params || {}).map(name => renderParameter(name, art.params[name], childEnv));
1090
1143
  if (parameters.length === 0)
@@ -1100,7 +1153,7 @@ function csnToCdl(csn, options) {
1100
1153
  * @param {CdlRenderEnvironment} env
1101
1154
  * @return {string}
1102
1155
  */
1103
- function renderParameter(parName, par, env) {
1156
+ function renderParameter( parName, par, env ) {
1104
1157
  env = envAddPath(env, [ 'params', parName ]);
1105
1158
  let result = `${renderAnnotationAssignmentsAndDocComment(par, env)}${env.indent}`;
1106
1159
  result += `${quoteIdIfRequired(parName)} : ${renderTypeReferenceAndProps(par, env)}`;
@@ -1117,7 +1170,7 @@ function csnToCdl(csn, options) {
1117
1170
  * @param {String} [artType] - used for rendering csn.vocabularies, as the annotations there do not have a kind.
1118
1171
  * @return {string}
1119
1172
  */
1120
- function renderTypeOrAnnotation(artifactName, art, env, artType) {
1173
+ function renderTypeOrAnnotation( artifactName, art, env, artType ) {
1121
1174
  let result = renderAnnotationAssignmentsAndDocComment(art, env);
1122
1175
  result += `${env.indent + (artType || art.$syntax || art.kind )} ${renderArtifactName(artifactName)}`;
1123
1176
  if (art.includes)
@@ -1142,7 +1195,7 @@ function csnToCdl(csn, options) {
1142
1195
  * - `noAnnoCollect` Do not collect annotations of sub-elements.
1143
1196
  * @return {string}
1144
1197
  */
1145
- function renderTypeReferenceAndProps(artifact, env, config = {}) {
1198
+ function renderTypeReferenceAndProps( artifact, env, config = {} ) {
1146
1199
  let result = '';
1147
1200
  const { typeRefOnly, noAnnoCollect } = config;
1148
1201
  let isTypeDef = env.path?.length === 2; // e.g [ 'definitions', typeDef ];
@@ -1195,8 +1248,7 @@ function csnToCdl(csn, options) {
1195
1248
 
1196
1249
  // ON-condition (if any)
1197
1250
  if (artifact.on)
1198
- result += ` on ${renderExpr(artifact.on, env, true, true)}`;
1199
-
1251
+ result += ` on ${exprRenderer.renderExpr(artifact.on, env)}`;
1200
1252
 
1201
1253
  // Foreign keys (if any, unless we also have an ON_condition (which means we have been transformed from managed to unmanaged)
1202
1254
  if (artifact.keys && !artifact.on)
@@ -1237,7 +1289,7 @@ function csnToCdl(csn, options) {
1237
1289
  if (!isTypeDef) // NOT NULL not possible for not-arrayed type definitions
1238
1290
  result += renderNullability(artifact);
1239
1291
  if (artifact.default)
1240
- result += ` default ${renderExpr(artifact.default, env)}`;
1292
+ result += ` default ${exprRenderer.renderExpr(artifact.default, env)}`;
1241
1293
 
1242
1294
  return result;
1243
1295
  }
@@ -1249,10 +1301,10 @@ function csnToCdl(csn, options) {
1249
1301
  * @param {CdlRenderEnvironment} env
1250
1302
  * @return {string}
1251
1303
  */
1252
- function renderRedirectedTo(art, env) {
1253
- let result = `redirected to ${quotePathIfRequired(art.target)}`;
1304
+ function renderRedirectedTo( art, env ) {
1305
+ let result = `redirected to ${renderDefinitionReference(art.target)}`;
1254
1306
  if (art.on)
1255
- result += ` on ${renderExpr(art.on, env, true, true)}`;
1307
+ result += ` on ${exprRenderer.renderExpr(art.on, env)}`;
1256
1308
  else if (art.keys)
1257
1309
  result += ` { ${Object.keys(art.keys).map(name => renderForeignKey(art.keys[name], env)).join(', ')} }`;
1258
1310
  return result;
@@ -1263,19 +1315,22 @@ function csnToCdl(csn, options) {
1263
1315
  * @param {CSN.Artifact} artWithType
1264
1316
  * @return {string}
1265
1317
  */
1266
- function renderNamedTypeWithParameters(artWithType) {
1318
+ function renderNamedTypeWithParameters( artWithType ) {
1267
1319
  let result = '';
1268
1320
 
1269
1321
  if (isBuiltinType(artWithType.type)) {
1270
- // If there is a user-defined type with the same short name (cds.Integer -> Integer),
1271
- // we render the full name, including the leading "cds."
1272
- if (csn.definitions[artWithType.type.slice(4)])
1322
+ // If there is a user-defined type that starts with the same short name
1323
+ // (cds.Integer -> Integer), we render the full name, including the leading "cds."
1324
+ const shortHand = artWithType.type.slice(4);
1325
+ if (shortHand.startsWith('hana.') && hanaRequiresAbsolutePath)
1326
+ result += artWithType.type;
1327
+ else if (usings.available.includes(shortHand))
1273
1328
  result += artWithType.type;
1274
1329
  else
1275
- result += artWithType.type.slice(4);
1330
+ result += shortHand;
1276
1331
  }
1277
1332
  else {
1278
- result += renderArtifactName(artWithType.type);
1333
+ result += renderDefinitionReference(artWithType.type);
1279
1334
  }
1280
1335
 
1281
1336
  result += renderTypeParameters(artWithType);
@@ -1290,35 +1345,15 @@ function csnToCdl(csn, options) {
1290
1345
  * @param {CdlRenderEnvironment} env
1291
1346
  * @return {string}
1292
1347
  */
1293
- function renderEnum(enumPart, env) {
1348
+ function renderEnum( enumPart, env ) {
1294
1349
  let result = ' enum {\n';
1295
1350
  const childEnv = increaseIndent(env);
1296
1351
  for (const name in enumPart)
1297
- result += renderEnumElement(name, enumPart[name], childEnv);
1352
+ result += renderElement(name, enumPart[name], childEnv);
1298
1353
  result += `${env.indent}}`;
1299
1354
  return result;
1300
1355
  }
1301
1356
 
1302
- /**
1303
- * Render the element of a `<type> enum {}` structure.
1304
- *
1305
- * @param {string} name
1306
- * @param {CSN.EnumValue} enumValue
1307
- * @param {CdlRenderEnvironment} env
1308
- * @return {string}
1309
- */
1310
- function renderEnumElement(name, enumValue, env) {
1311
- let result = '';
1312
- result += renderAnnotationAssignmentsAndDocComment(enumValue, env);
1313
- result += env.indent + quoteIdIfRequired(name);
1314
- if (enumValue.val !== undefined)
1315
- result += ` = ${renderExpr(enumValue, env)}`;
1316
- else if (enumValue['#'] !== undefined)
1317
- result += ` = #${enumValue['#']}`;
1318
- result += ';\n';
1319
- return result;
1320
- }
1321
-
1322
1357
  /**
1323
1358
  * Render an annotation value (somewhat like a simplified expression, with slightly different
1324
1359
  * representation)
@@ -1326,7 +1361,7 @@ function csnToCdl(csn, options) {
1326
1361
  * @param {any} x
1327
1362
  * @param {CdlRenderEnvironment} env
1328
1363
  */
1329
- function renderAnnotationValue(x, env) {
1364
+ function renderAnnotationValue( x, env ) {
1330
1365
  if (Array.isArray(x)) {
1331
1366
  // Render array parts as values. Spaces required if last array value is
1332
1367
  // a delimited identifier.
@@ -1373,11 +1408,10 @@ function csnToCdl(csn, options) {
1373
1408
  *
1374
1409
  * @param {string|object} s
1375
1410
  * @param {number} idx
1376
- * @param {boolean} inline
1377
1411
  * @param {object} env
1378
1412
  * @returns {string}
1379
1413
  */
1380
- function renderPathStep(s, idx, inline, env) {
1414
+ function renderPathStep( s, idx, env ) {
1381
1415
  // Simple id or absolute name
1382
1416
  if (typeof s === 'string') {
1383
1417
  // In first path position, do not quote $projection and magic $-variables like CURRENT_DATE, $now etc.
@@ -1405,7 +1439,7 @@ function csnToCdl(csn, options) {
1405
1439
  if (s.where) {
1406
1440
  // Filter, possibly with cardinality
1407
1441
  const cardinality = s.cardinality ? (`${s.cardinality.max}: `) : '';
1408
- const expr = renderExpr(s.where, env, inline, true);
1442
+ const expr = exprRenderer.renderExpr(s.where, env);
1409
1443
  result += `[${cardinality}${expr}]`;
1410
1444
  }
1411
1445
 
@@ -1424,7 +1458,7 @@ function csnToCdl(csn, options) {
1424
1458
  * @param {CdlRenderEnvironment} env
1425
1459
  * @returns {string}
1426
1460
  */
1427
- function renderArguments(node, sep, env) {
1461
+ function renderArguments( node, sep, env ) {
1428
1462
  if (!node.args)
1429
1463
  return '';
1430
1464
  else if (Array.isArray(node.args))
@@ -1443,7 +1477,7 @@ function csnToCdl(csn, options) {
1443
1477
  * @param {CdlRenderEnvironment} env
1444
1478
  * @returns {string}
1445
1479
  */
1446
- function renderNamedArguments(node, separator, env) {
1480
+ function renderNamedArguments( node, separator, env ) {
1447
1481
  return Object.keys(node.args).map(function renderNamedArgument(key) {
1448
1482
  return `${quoteIdIfRequired(key, env.additionalKeywords)} ${separator} ${renderArgument(node.args[key], env)}`;
1449
1483
  }).join(', ');
@@ -1456,7 +1490,7 @@ function csnToCdl(csn, options) {
1456
1490
  * @param {CdlRenderEnvironment} env
1457
1491
  * @returns {string}
1458
1492
  */
1459
- function renderPositionalArguments(node, env) {
1493
+ function renderPositionalArguments( node, env ) {
1460
1494
  if (!node.args)
1461
1495
  return '';
1462
1496
  const func = node.func?.toUpperCase();
@@ -1477,11 +1511,13 @@ function csnToCdl(csn, options) {
1477
1511
  * @param {string[]} additionalKeywords
1478
1512
  * @return {string}
1479
1513
  */
1480
- function renderArgument(arg, env, additionalKeywords = []) {
1514
+ function renderArgument( arg, env, additionalKeywords = [] ) {
1481
1515
  // If the argument is a xpr with e.g. `=`, it may require parentheses.
1482
- // For nested xpr, `renderExpr()` will already add parentheses.
1516
+ // For nested xpr, `exprRenderer.renderExpr()` will already add parentheses.
1483
1517
  env = { ...env, additionalKeywords };
1484
- return renderExpr(arg, env, true, !isSimpleFunctionExpression(arg && arg.xpr, additionalKeywords), true);
1518
+ if (isSimpleFunctionExpression(arg && arg.xpr, additionalKeywords))
1519
+ return exprRenderer.renderExpr(arg, env);
1520
+ return exprRenderer.renderSubExpr(arg, env);
1485
1521
  }
1486
1522
 
1487
1523
  /**
@@ -1490,7 +1526,7 @@ function csnToCdl(csn, options) {
1490
1526
  * @param artifact
1491
1527
  * @returns {string}
1492
1528
  */
1493
- function renderCardinality(artifact) {
1529
+ function renderCardinality( artifact ) {
1494
1530
  if (isSimpleCardinality(artifact.cardinality))
1495
1531
  return renderSimpleCardinality(artifact);
1496
1532
  return renderBracketCardinality(artifact);
@@ -1502,7 +1538,7 @@ function csnToCdl(csn, options) {
1502
1538
  * @param {CSN.Artifact} art
1503
1539
  * @return {string}
1504
1540
  */
1505
- function renderBracketCardinality(art) {
1541
+ function renderBracketCardinality( art ) {
1506
1542
  const isComp = art.type === 'cds.Composition';
1507
1543
  const suffix = (isComp ? ' of ' : ' to ');
1508
1544
  const card = art.cardinality;
@@ -1530,7 +1566,7 @@ function csnToCdl(csn, options) {
1530
1566
  * @param {CSN.Cardinality} cardinality
1531
1567
  * @return {boolean}
1532
1568
  */
1533
- function isSimpleCardinality(cardinality) {
1569
+ function isSimpleCardinality( cardinality ) {
1534
1570
  return !cardinality || (
1535
1571
  cardinality.min === undefined &&
1536
1572
  cardinality.src === undefined &&
@@ -1546,7 +1582,7 @@ function csnToCdl(csn, options) {
1546
1582
  * @param {CSN.Element} elem
1547
1583
  * @return {string}
1548
1584
  */
1549
- function renderSimpleCardinality(elem) {
1585
+ function renderSimpleCardinality( elem ) {
1550
1586
  let result = (elem.type === 'cds.Association' ? ' to ' : ' of ');
1551
1587
  if (!elem.cardinality)
1552
1588
  return result;
@@ -1558,7 +1594,7 @@ function csnToCdl(csn, options) {
1558
1594
  }
1559
1595
 
1560
1596
  // Render the nullability of an element or parameter (can be unset, true, or false)
1561
- function renderNullability(obj /* , env */) {
1597
+ function renderNullability( obj /* , env */) {
1562
1598
  if (obj.notNull === undefined) {
1563
1599
  // Attribute not set at all
1564
1600
  return '';
@@ -1573,9 +1609,9 @@ function csnToCdl(csn, options) {
1573
1609
  * @param {CdlRenderEnvironment} env
1574
1610
  * @return {string}
1575
1611
  */
1576
- function renderForeignKey(fKey, env) {
1612
+ function renderForeignKey( fKey, env ) {
1577
1613
  const alias = fKey.as ? (` as ${fKey.as}`) : '';
1578
- return renderExpr(fKey, env) + alias;
1614
+ return exprRenderer.renderExpr(fKey, env) + alias;
1579
1615
  }
1580
1616
 
1581
1617
  /**
@@ -1583,18 +1619,22 @@ function csnToCdl(csn, options) {
1583
1619
  * length, precision and scale (even if incomplete), plus any other unknown ones.
1584
1620
  *
1585
1621
  * @param {CSN.Artifact} artWithType
1622
+ * @param {boolean} noShortVersion If true, parameters will not be shortened, e.g. `(10)`
1623
+ * for length instead of `(length: 10)`.
1586
1624
  * @returns {string}
1587
1625
  */
1588
- function renderTypeParameters(artWithType) {
1626
+ function renderTypeParameters( artWithType, noShortVersion = false ) {
1589
1627
  const params = typeParameters.list.filter(param => artWithType[param] !== undefined);
1590
1628
  if (params.length === 0)
1591
1629
  return '';
1592
1630
 
1631
+ if (!noShortVersion) {
1593
1632
  // Special cases for 1 or 2 arguments.
1594
- if (params.length === 1 && artWithType.length !== undefined)
1595
- return `(${artWithType.length})`;
1596
- if (params.length === 2 && artWithType.precision !== undefined && artWithType.scale !== undefined)
1597
- return `(${artWithType.precision}, ${artWithType.scale})`;
1633
+ if (params.length === 1 && artWithType.length !== undefined)
1634
+ return `(${artWithType.length})`;
1635
+ if (params.length === 2 && artWithType.precision !== undefined && artWithType.scale !== undefined)
1636
+ return `(${artWithType.precision}, ${artWithType.scale})`;
1637
+ }
1598
1638
 
1599
1639
  // Render named params
1600
1640
  const renderedParams = [];
@@ -1612,7 +1652,7 @@ function csnToCdl(csn, options) {
1612
1652
  * @param {{parens: boolean}} [config] Config for renderAnnotationAssignment()
1613
1653
  * @return {string}
1614
1654
  */
1615
- function renderAnnotationAssignmentsAndDocComment(obj, env, config) {
1655
+ function renderAnnotationAssignmentsAndDocComment( obj, env, config ) {
1616
1656
  let result = renderDocComment(obj, env);
1617
1657
  for (const name in obj) {
1618
1658
  if (name.startsWith('@'))
@@ -1632,7 +1672,7 @@ function csnToCdl(csn, options) {
1632
1672
  * @param {object} [config] parens: Whether the annotation assignment must be surrounded by parentheses.
1633
1673
  * @return {string} Rendered annotation, possibly quoted: `@![A.B.C#foo.C]: value`
1634
1674
  */
1635
- function renderAnnotationAssignment(anno, name, env, config = { parens: false }) {
1675
+ function renderAnnotationAssignment( anno, name, env, config = { parens: false } ) {
1636
1676
  name = name.substring(1);
1637
1677
  // Take the annotation assignment apart into <nameBeforeVariant>#<variantAndRest>
1638
1678
  const parts = name.split('#');
@@ -1671,104 +1711,91 @@ function csnToCdl(csn, options) {
1671
1711
  * @param {string} artifactName Artifact name to render
1672
1712
  * @return {string} Artifact name ready for rendering
1673
1713
  */
1674
- function renderArtifactName(artifactName) {
1714
+ function renderArtifactName( artifactName ) {
1675
1715
  return quotePathIfRequired(artifactName);
1676
1716
  }
1677
1717
 
1678
1718
  /**
1679
- * Render a function expression.
1719
+ * Render the name of a definition. Ensures the first segment of the name
1720
+ * is available in the rendered CDL. Otherwise a USING is added.
1680
1721
  *
1681
- * @param {object} obj Object with .func and optionally .args
1682
- * @param {CdlRenderEnvironment} env
1683
- * @returns {string}
1722
+ * @param {string} name
1723
+ * @return {string}
1684
1724
  */
1685
- function renderFuncExpr( obj, env ) {
1686
- if (keywords.cdl_functions.includes(obj.func.toUpperCase()))
1687
- return obj.func;
1688
- const name = identifierRegex.test(obj.func) ? obj.func : quote(obj.func);
1689
- return `${name}(${renderArguments( obj, '=>', env )})`;
1725
+ function renderDefinitionReference( name ) {
1726
+ usings.addIfRequired(name);
1727
+ return quotePathIfRequired(name);
1690
1728
  }
1691
1729
 
1692
1730
  /**
1693
- * Render an expression.
1694
- *
1695
- * @param {any} expr
1696
- * @param {CdlRenderEnvironment} exprEnv
1697
- * @param {boolean} [isInline]
1698
- * @param {boolean} [isNestedExpr]
1699
- * @param {boolean} [alwaysRenderCast]
1700
- * @returns {string}
1731
+ * @param {string[]} includes
1732
+ * @return {string}
1701
1733
  */
1702
- function renderExpr(expr, exprEnv, isInline, isNestedExpr, alwaysRenderCast) {
1703
- if (!_renderExpr) {
1704
- _renderExpr = getExpressionRenderer({
1705
- finalize(x) {
1706
- return x;
1707
- },
1708
- explicitTypeCast(x, env) {
1709
- const typeRef = renderTypeReferenceAndProps(x.cast, env, { typeRefOnly: true, noAnnoCollect: true });
1710
- const arg = { ...x, cast: null }; // "arg" without cast to avoid recursion.
1711
- return `cast(${renderArgument(arg, env)} as ${typeRef})`;
1712
- },
1713
- val(x, env) {
1714
- // Literal value, possibly with explicit 'literal' property
1715
- switch (x.literal || typeof x.val) {
1716
- case 'number':
1717
- case 'boolean':
1718
- case 'null':
1719
- return x.val;
1720
- case 'x':
1721
- case 'date':
1722
- case 'time':
1723
- case 'timestamp':
1724
- return `${x.literal}'${x.val}'`;
1725
- case 'string':
1726
- return renderString(x.val, env);
1727
- case 'object':
1728
- if (x.val === null)
1729
- return 'null';
1730
- // otherwise fall through to
1731
- default:
1732
- throw new ModelError(`Unknown literal or type: ${JSON.stringify(x)}`);
1733
- }
1734
- },
1735
- aliasOnly(x, _env) {
1736
- return x.as;
1737
- },
1738
- enum(x) {
1739
- return `#${x['#']}`;
1740
- },
1741
- ref(x, env) {
1742
- const { inline } = this;
1743
- return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, inline, env)).join('.')}`;
1744
- },
1745
- windowFunction(x, env) {
1746
- const funcDef = renderFuncExpr(x, env);
1747
- const windowFunctionOperator = x.xpr.shift(); // OVER ...
1748
- return `${funcDef} ${windowFunctionOperator} ( ${renderExpr(x.xpr, env, true)} )`;
1749
- },
1750
- func(x, env) {
1751
- return renderFuncExpr(x, env);
1752
- },
1753
- xpr(x, env) {
1754
- if (this.nestedExpr && !x.cast || x.xpr.some(s => s === 'exists'))
1755
- return `(${renderExpr(x.xpr, env, this.inline, true)})`;
1756
- return renderExpr(x.xpr, env, this.inline, true);
1757
- },
1758
- // Sub-queries in expressions need to be in parentheses, otherwise
1759
- // left-associativity of UNIONS may result in different results.
1760
- // For example: `select from E where id in (select from E union select from E);`:
1761
- // Without parentheses, it would be different query.
1762
- SET(x, env) {
1763
- return `(${renderQuery(x, false, 'view', increaseIndent(env))})`;
1764
- },
1765
- SELECT(x, env) {
1766
- return `(${renderQuery(x, false, 'view', increaseIndent(env))})`;
1767
- },
1768
- });
1769
- }
1734
+ function renderIncludes( includes ) {
1735
+ return ` : ${includes.map(name => renderDefinitionReference(name)).join(', ')}`;
1736
+ }
1770
1737
 
1771
- return _renderExpr(expr, exprEnv, isInline, isNestedExpr, alwaysRenderCast);
1738
+ function createCdlExpressionRenderer() {
1739
+ return createExpressionRenderer({
1740
+ finalize: x => x,
1741
+ typeCast(x) {
1742
+ const typeRef = renderTypeReferenceAndProps(x.cast, this.env, { typeRefOnly: true, noAnnoCollect: true });
1743
+ const arg = { ...x, cast: null }; // "arg" without cast to avoid recursion.
1744
+ return `cast(${renderArgument(arg, this.env)} as ${typeRef})`;
1745
+ },
1746
+ val(x) {
1747
+ // Literal value, possibly with explicit 'literal' property
1748
+ switch (x.literal || typeof x.val) {
1749
+ case 'number':
1750
+ case 'boolean':
1751
+ case 'null':
1752
+ return x.val;
1753
+ case 'x':
1754
+ case 'date':
1755
+ case 'time':
1756
+ case 'timestamp':
1757
+ return `${x.literal}'${x.val}'`;
1758
+ case 'string':
1759
+ return renderString(x.val, this.env);
1760
+ case 'object':
1761
+ if (x.val === null)
1762
+ return 'null';
1763
+ // otherwise fall through to
1764
+ default:
1765
+ throw new ModelError(`Unknown literal or type: ${JSON.stringify(x)}`);
1766
+ }
1767
+ },
1768
+ aliasOnly: x => x.as,
1769
+ enum: x => `#${x['#']}`,
1770
+ ref(x) {
1771
+ return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, this.env)).join('.')}`;
1772
+ },
1773
+ windowFunction(x) {
1774
+ const funcDef = this.func(x);
1775
+ return `${funcDef} ${this.renderExpr(x.xpr)}`; // xpr[0] is 'over'
1776
+ },
1777
+ func(x) {
1778
+ if (keywords.cdl_functions.includes(x.func.toUpperCase()))
1779
+ return x.func;
1780
+ const name = identifierRegex.test(x.func) ? x.func : quote(x.func);
1781
+ return `${name}(${renderArguments( x, '=>', this.env )})`;
1782
+ },
1783
+ xpr(x) {
1784
+ if (this.isNestedXpr && !x.cast || x.xpr.some(s => s === 'exists'))
1785
+ return `(${this.renderExpr(x.xpr)})`;
1786
+ return this.renderExpr(x.xpr);
1787
+ },
1788
+ // Sub-queries in expressions need to be in parentheses, otherwise
1789
+ // left-associativity of UNIONS may result in different results.
1790
+ // For example: `select from E where id in (select from E union select from E);`:
1791
+ // Without parentheses, it would be different query.
1792
+ SET(x) {
1793
+ return `(${renderQuery(x, false, 'view', increaseIndent(this.env))})`;
1794
+ },
1795
+ SELECT(x) {
1796
+ return `(${renderQuery(x, false, 'view', increaseIndent(this.env))})`;
1797
+ },
1798
+ });
1772
1799
  }
1773
1800
  }
1774
1801
 
@@ -1776,32 +1803,33 @@ function csnToCdl(csn, options) {
1776
1803
  * Returns a newly created default environment (which keeps track of indentation, required USING
1777
1804
  * declarations and name prefixes.
1778
1805
  *
1806
+ * @param {object} [values]
1779
1807
  * @return {CdlRenderEnvironment}
1780
1808
  */
1781
- function createEnv() {
1782
- return {
1809
+ function createEnv( values = {} ) {
1810
+ return Object.assign({
1783
1811
  // Current indentation string
1784
1812
  indent: '',
1785
1813
  path: null,
1786
1814
  artifactName: '',
1787
1815
  elementName: '',
1788
- };
1816
+ }, values);
1789
1817
  }
1790
1818
 
1791
- function envAddPath(env, path) {
1819
+ function envAddPath( env, path ) {
1792
1820
  return Object.assign({}, env, { path: [ ...env.path, ...path ] } );
1793
1821
  }
1794
- function envNewPath(env, path) {
1822
+ function envNewPath( env, path ) {
1795
1823
  return Object.assign({}, env, { path: [ ...path ] } );
1796
1824
  }
1797
1825
 
1798
1826
  /**
1799
1827
  * Returns a copy of 'env' with increased indentation (and reset name prefix)
1800
1828
  *
1801
- * @param {CdlRenderEnvironment} env
1802
- * @returns {CdlRenderEnvironment}
1829
+ * @param {object} env
1830
+ * @returns {object}
1803
1831
  */
1804
- function increaseIndent(env) {
1832
+ function increaseIndent( env ) {
1805
1833
  return Object.assign({}, env, { indent: `${env.indent} ` });
1806
1834
  }
1807
1835
 
@@ -1817,7 +1845,7 @@ function increaseIndent(env) {
1817
1845
  *
1818
1846
  * @todo For paths such as `E.key`, `key` does not have to be in quotes.
1819
1847
  */
1820
- function quotePathIfRequired(path) {
1848
+ function quotePathIfRequired( path ) {
1821
1849
  return path.split('.').map(step => quoteIdIfRequired(step)).join('.');
1822
1850
  }
1823
1851
 
@@ -1832,7 +1860,7 @@ function quotePathIfRequired(path) {
1832
1860
  * @param {string[]} [additionalKeywords]
1833
1861
  * @return {string}
1834
1862
  */
1835
- function quoteIdIfRequired(id, additionalKeywords) {
1863
+ function quoteIdIfRequired( id, additionalKeywords ) {
1836
1864
  // Quote if required for CDL
1837
1865
  if (requiresQuotingForCdl(id, additionalKeywords || []))
1838
1866
  return quote(id);
@@ -1848,7 +1876,7 @@ function quoteIdIfRequired(id, additionalKeywords) {
1848
1876
  * @param {string} anno
1849
1877
  * @returns {string}
1850
1878
  */
1851
- function quoteAnnotationPathIfRequired(anno) {
1879
+ function quoteAnnotationPathIfRequired( anno ) {
1852
1880
  return anno.split('.').map((segment) => {
1853
1881
  if (segment.startsWith('@'))
1854
1882
  return `@${quoteIdIfRequired(segment.slice(1))}`;
@@ -1862,7 +1890,7 @@ function quoteAnnotationPathIfRequired(anno) {
1862
1890
  * @param id
1863
1891
  * @returns {string}
1864
1892
  */
1865
- function quote(id) {
1893
+ function quote( id ) {
1866
1894
  return `![${id.replace(/]/g, ']]')}]`;
1867
1895
  }
1868
1896
 
@@ -1878,7 +1906,7 @@ function quote(id) {
1878
1906
  * @param {string[]} [additionalKeywords]
1879
1907
  * @return {boolean}
1880
1908
  */
1881
- function requiresQuotingForCdl(id, additionalKeywords) {
1909
+ function requiresQuotingForCdl( id, additionalKeywords ) {
1882
1910
  return !identifierRegex.test(id) ||
1883
1911
  keywords.cdl.includes(id.toUpperCase()) ||
1884
1912
  keywords.cdl_functions.includes(id.toUpperCase()) ||
@@ -1887,16 +1915,16 @@ function requiresQuotingForCdl(id, additionalKeywords) {
1887
1915
 
1888
1916
  const functionExpressionOperatorsRequireParentheses = [
1889
1917
  // Antlr rule 'condition', 'conditionAnd'
1890
- 'and', 'or',
1918
+ 'AND', 'OR',
1891
1919
 
1892
1920
  // Antlr rule 'conditionTerm'
1893
1921
  '=', '<>', '>', '>=', '<', '<=', '!=',
1894
1922
  // These are not forbidden, since they must be preceded by one of the comparators above.
1895
1923
  // 'any', 'some', 'all',
1896
1924
 
1897
- 'is', 'in', 'not', 'null', 'exists',
1925
+ 'IS', 'IN', 'NOT', 'NULL', 'EXISTS',
1898
1926
  // Antlr rule 'predicate'
1899
- 'between', 'like', 'escape',
1927
+ 'BETWEEN', 'LIKE', 'ESCAPE',
1900
1928
  ];
1901
1929
 
1902
1930
  /**
@@ -1921,10 +1949,10 @@ const functionExpressionOperatorsRequireParentheses = [
1921
1949
  * @param {string[]} additionalAllowedKeywords
1922
1950
  * @return {boolean}
1923
1951
  */
1924
- function isSimpleFunctionExpression(xpr, additionalAllowedKeywords = []) {
1952
+ function isSimpleFunctionExpression( xpr, additionalAllowedKeywords = [] ) {
1925
1953
  return !xpr || xpr.every(val => typeof val !== 'string' ||
1926
- (additionalAllowedKeywords.includes(val) ||
1927
- !functionExpressionOperatorsRequireParentheses.includes(val.toLowerCase())));
1954
+ (additionalAllowedKeywords.includes(val.toUpperCase()) ||
1955
+ !functionExpressionOperatorsRequireParentheses.includes(val.toUpperCase())));
1928
1956
  }
1929
1957
 
1930
1958
  /**
@@ -1938,7 +1966,7 @@ function isSimpleFunctionExpression(xpr, additionalAllowedKeywords = []) {
1938
1966
  * @param {number} argumentIndex
1939
1967
  * @returns {string[]}
1940
1968
  */
1941
- function getKeywordsForSpecialFunctionArgument(funcName, argumentIndex) {
1969
+ function getKeywordsForSpecialFunctionArgument( funcName, argumentIndex ) {
1942
1970
  const f = specialFunctions[funcName] && specialFunctions[funcName][argumentIndex];
1943
1971
  if (!f)
1944
1972
  return [];
@@ -1952,14 +1980,6 @@ function getKeywordsForSpecialFunctionArgument(funcName, argumentIndex) {
1952
1980
  return additionalKeywords;
1953
1981
  }
1954
1982
 
1955
- /**
1956
- * @param {string[]} includes
1957
- * @return {string}
1958
- */
1959
- function renderIncludes(includes) {
1960
- return ` : ${includes.map(name => quotePathIfRequired(name)).join(', ')}`;
1961
- }
1962
-
1963
1983
  /**
1964
1984
  * Render the given string. Uses back-tick strings.
1965
1985
  * env is used for indentation of three-back-tick strings.
@@ -1968,7 +1988,7 @@ function renderIncludes(includes) {
1968
1988
  * @param env
1969
1989
  * @returns {string}
1970
1990
  */
1971
- function renderString(str, env) {
1991
+ function renderString( str, env ) {
1972
1992
  if (isSimpleString(str))
1973
1993
  return `'${str.replace(/'/g, '\'\'')}'`;
1974
1994
 
@@ -2013,7 +2033,7 @@ function renderString(str, env) {
2013
2033
  }
2014
2034
 
2015
2035
  /** @param {number} codePoint */
2016
- function hexEscape(codePoint) {
2036
+ function hexEscape( codePoint ) {
2017
2037
  const hex = codePoint.toString(16);
2018
2038
  return `\\u{${hex}}`;
2019
2039
  }
@@ -2022,7 +2042,7 @@ function hexEscape(codePoint) {
2022
2042
  * Returns true if the given string can be represented by using single quotes.
2023
2043
  * @param {string} str
2024
2044
  */
2025
- function isSimpleString(str) {
2045
+ function isSimpleString( str ) {
2026
2046
  // A single-line string allows everything except certain line separators/breaks.
2027
2047
  // See ANTLR grammar for specifics.
2028
2048
  // Furthermore, if control characters are used, we escape them,
@@ -2034,13 +2054,28 @@ function isSimpleString(str) {
2034
2054
  !hasUnpairedUnicodeSurrogate(str));
2035
2055
  }
2036
2056
 
2057
+ /**
2058
+ * Get a list of top-level artifact names, which are not in contexts/usings/, i.e. those
2059
+ * before the first dot ('.'). For example for `S.E.F`, `S` is used.
2060
+ *
2061
+ * @param {CSN.Model} csn
2062
+ * @return {string[]}
2063
+ */
2064
+ function availableFirstPathSteps( csn ) {
2065
+ if (!csn.definitions)
2066
+ return [];
2067
+ const unique = new Set(Object.keys(csn.definitions).map(name => name.split('.')[0]));
2068
+ return Array.from(unique);
2069
+ }
2070
+
2037
2071
  /**
2038
2072
  * @typedef CdlRenderEnvironment Rendering environment used throughout the render process.
2039
2073
  *
2040
2074
  * @property {string} indent Current indentation as a string, e.g. ' ' for two spaces.
2041
- * @property {string[]} [path] CSN path to the current artifact
2075
+ * @property {CSN.Path} [path] CSN path to the current artifact
2042
2076
  * @property {string} [artifactName] Name of the artifact - set in renderArtifact
2043
2077
  * @property {string} [elementName] Name of the element being rendered - set in renderElement
2078
+ * @property {string[]} [additionalKeywords] For function rendering: Words that are also keywords.
2044
2079
  */
2045
2080
 
2046
2081
  module.exports = { csnToCdl };