@sap/cds-compiler 4.7.4 → 4.8.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 (94) hide show
  1. package/CHANGELOG.md +47 -2
  2. package/bin/cdsc.js +15 -1
  3. package/bin/cdshi.js +13 -3
  4. package/doc/CHANGELOG_BETA.md +5 -1
  5. package/lib/api/main.js +61 -23
  6. package/lib/api/options.js +40 -0
  7. package/lib/base/builtins.js +89 -0
  8. package/lib/base/keywords.js +5 -1
  9. package/lib/base/location.js +91 -14
  10. package/lib/base/message-registry.js +50 -33
  11. package/lib/base/messages.js +71 -16
  12. package/lib/base/model.js +0 -2
  13. package/lib/checks/actionsFunctions.js +1 -1
  14. package/lib/checks/elements.js +2 -1
  15. package/lib/checks/enricher.js +2 -2
  16. package/lib/checks/queryNoDbArtifacts.js +2 -1
  17. package/lib/checks/utils.js +1 -1
  18. package/lib/checks/validator.js +6 -22
  19. package/lib/compiler/assert-consistency.js +3 -5
  20. package/lib/compiler/builtins.js +0 -74
  21. package/lib/compiler/checks.js +61 -11
  22. package/lib/compiler/define.js +3 -3
  23. package/lib/compiler/extend.js +2 -2
  24. package/lib/compiler/index.js +9 -9
  25. package/lib/compiler/populate.js +13 -5
  26. package/lib/compiler/propagator.js +3 -0
  27. package/lib/compiler/resolve.js +6 -20
  28. package/lib/compiler/shared.js +1 -1
  29. package/lib/compiler/tweak-assocs.js +2 -2
  30. package/lib/compiler/utils.js +3 -3
  31. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  32. package/lib/edm/annotations/edmJson.js +7 -5
  33. package/lib/edm/annotations/genericTranslation.js +113 -55
  34. package/lib/edm/csn2edm.js +25 -9
  35. package/lib/edm/edm.js +3 -3
  36. package/lib/edm/edmInboundChecks.js +24 -5
  37. package/lib/edm/edmPreprocessor.js +46 -20
  38. package/lib/edm/edmUtils.js +3 -16
  39. package/lib/gen/Dictionary.json +9 -0
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +1941 -1850
  43. package/lib/json/csnVersion.js +7 -4
  44. package/lib/json/from-csn.js +8 -7
  45. package/lib/json/to-csn.js +12 -7
  46. package/lib/language/antlrParser.js +1 -1
  47. package/lib/language/genericAntlrParser.js +9 -10
  48. package/lib/language/multiLineStringParser.js +2 -2
  49. package/lib/language/textUtils.js +1 -1
  50. package/lib/main.d.ts +23 -0
  51. package/lib/main.js +8 -1
  52. package/lib/model/cloneCsn.js +15 -6
  53. package/lib/model/csnRefs.js +141 -35
  54. package/lib/model/csnUtils.js +1 -4
  55. package/lib/model/enrichCsn.js +1 -1
  56. package/lib/modelCompare/compare.js +106 -92
  57. package/lib/optionProcessor.js +23 -1
  58. package/lib/render/toCdl.js +3 -2
  59. package/lib/render/toHdbcds.js +4 -48
  60. package/lib/render/toSql.js +6 -3
  61. package/lib/transform/addTenantFields.js +58 -35
  62. package/lib/transform/db/applyTransformations.js +1 -1
  63. package/lib/transform/db/expansion.js +3 -0
  64. package/lib/transform/db/flattening.js +71 -46
  65. package/lib/transform/db/views.js +1 -4
  66. package/lib/transform/draft/odata.js +16 -17
  67. package/lib/transform/effective/main.js +6 -3
  68. package/lib/transform/effective/misc.js +18 -8
  69. package/lib/transform/effective/types.js +4 -3
  70. package/lib/transform/forOdata.js +8 -7
  71. package/lib/transform/forRelationalDB.js +103 -112
  72. package/lib/transform/odata/flattening.js +82 -44
  73. package/lib/transform/odata/toFinalBaseType.js +9 -25
  74. package/lib/transform/odata/typesExposure.js +28 -15
  75. package/lib/transform/parseExpr.js +0 -3
  76. package/lib/transform/transformUtils.js +12 -8
  77. package/lib/transform/translateAssocsToJoins.js +2 -2
  78. package/lib/transform/universalCsn/coreComputed.js +2 -1
  79. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
  80. package/package.json +2 -2
  81. package/share/messages/README.md +4 -0
  82. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  83. package/share/messages/check-proper-type-of.md +1 -1
  84. package/share/messages/def-duplicate-autoexposed.md +1 -1
  85. package/share/messages/extend-repeated-intralayer.md +3 -16
  86. package/share/messages/extend-unrelated-layer.md +1 -1
  87. package/share/messages/message-explanations.json +1 -0
  88. package/share/messages/redirected-to-ambiguous.md +1 -1
  89. package/share/messages/redirected-to-complex.md +1 -1
  90. package/share/messages/redirected-to-unrelated.md +1 -1
  91. package/share/messages/rewrite-not-supported.md +1 -1
  92. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  93. package/share/messages/type-missing-enum-value.md +59 -0
  94. package/share/messages/wildcard-excluding-one.md +1 -1
@@ -8,8 +8,10 @@
8
8
  // (Note: the SQL name mapping mode is not reflected in the content of the csn, the version only
9
9
  // signals which default name mapping a backend has to use)
10
10
  // Historic versions:
11
- // 0.0.1 : Used by HANA CDS for its CSN output (incomplete, not well defined, quite different from CDX ...)
12
- // 0.0.2 : CDX in the initial versions with old-style CSN, default for SQL name mapping is 'quoted'
11
+ // 0.0.1 : Used by SAP HANA CDS for its CSN output
12
+ // (incomplete, not well defined, quite different from CDX ...)
13
+ // 0.0.2 : CDX in the initial versions with old-style CSN, default for SQL
14
+ // name mapping is 'quoted'
13
15
  // 0.0.99 : Like 0.0.2, but with new-style CSN
14
16
  // Versions that are currently produced by compiler:
15
17
  // 0.1.0 : Like 0.0.2, default for SQL name mapping is 'plain'
@@ -21,15 +23,16 @@
21
23
  const newCSNVersions = [ '0.1.99', '0.2', '0.2.0', '1.0', '2.0' ];
22
24
 
23
25
  // Check if new-style CSN is requested, i.e. versions >= 0.1.99
24
- function isNewCSN(csn, options) {
26
+ function isNewCSN( csn, options ) {
25
27
  return !( options?.newCsn === false ||
26
28
  (csn.version && !newCSNVersions.includes(csn.version.csn)) ||
27
29
  (csn.$version && !newCSNVersions.includes(csn.$version)));
28
30
  }
29
31
 
30
- function checkCSNVersion(csn, options) {
32
+ function checkCSNVersion( csn, options ) {
31
33
  if (!isNewCSN(csn, options)) {
32
34
  // the new transformer works only with new CSN
35
+ // eslint-disable-next-line global-require
33
36
  const { makeMessageFunction } = require('../base/messages');
34
37
  const { error, throwWithAnyError } = makeMessageFunction(csn, options);
35
38
 
@@ -119,10 +119,11 @@
119
119
  */
120
120
 
121
121
  const { dictAdd } = require('../base/dictionaries');
122
- // TODO: move parts of lib/compiler/builtins to some lib/base/…:
123
- const { isAnnotationExpression, quotedLiteralPatterns } = require('../compiler/builtins');
122
+ const { quotedLiteralPatterns } = require('../compiler/builtins');
123
+ const { isAnnotationExpression } = require('../base/builtins');
124
124
  const { CompilerAssertion } = require('../base/error');
125
- const { XsnSource, CsnLocation } = require('../compiler/classes');
125
+ const { Location } = require('../base/location');
126
+ const { XsnSource } = require('../compiler/xsn-model');
126
127
 
127
128
  const $location = Symbol.for('cds.$location');
128
129
 
@@ -1997,7 +1998,7 @@ function replaceZeroValue( spec, msgVariant, newValue ) {
1997
1998
  function location( enforceJsonPos ) {
1998
1999
  return !enforceJsonPos && dollarLocations.length &&
1999
2000
  dollarLocations[dollarLocations.length - 1] || {
2000
- __proto__: CsnLocation.prototype,
2001
+ __proto__: Location.prototype,
2001
2002
  file: csnFilename,
2002
2003
  line: virtualLine,
2003
2004
  col: 0,
@@ -2010,7 +2011,7 @@ function pushLocation( obj ) {
2010
2011
  if (loc === undefined)
2011
2012
  return;
2012
2013
  if (loc && typeof loc === 'object' && !Array.isArray( loc )) {
2013
- dollarLocations.push( loc.line ? { __proto__: CsnLocation.prototype, ...loc } : null );
2014
+ dollarLocations.push( loc.line ? { __proto__: Location.prototype, ...loc } : null );
2014
2015
  return;
2015
2016
  }
2016
2017
  else if (!loc || typeof loc !== 'string') {
@@ -2028,7 +2029,7 @@ function pushLocation( obj ) {
2028
2029
  const column = m[2] && Number( m[2] ) || 0;
2029
2030
  const file = loc.substring( 0, m.index );
2030
2031
  dollarLocations.push({
2031
- __proto__: CsnLocation.prototype, file, line, col: column,
2032
+ __proto__: Location.prototype, file, line, col: column,
2032
2033
  } );
2033
2034
  }
2034
2035
  }
@@ -2123,7 +2124,7 @@ function parse( source, filename = 'csn.json', options = {}, messageFunctions =
2123
2124
  }
2124
2125
  column = end - eol + 1;
2125
2126
  }
2126
- const loc = new CsnLocation(
2127
+ const loc = new Location(
2127
2128
  filename,
2128
2129
  line,
2129
2130
  column
@@ -232,13 +232,15 @@ function compactModel( model, options = model.options || {} ) {
232
232
  if (using.length)
233
233
  csn.requires = using;
234
234
  }
235
- // 'namespace' for complete model is 'namespace' of first source
236
- // (not a really useful property at all, avoids XSN inspection by Umbrella)
237
- for (const first in srcDict) {
238
- const { namespace } = srcDict[first];
239
- if (namespace && namespace.path)
240
- csn.namespace = pathName( namespace.path );
241
- break;
235
+ if (!isBetaEnabled(options, 'v5preview')) {
236
+ // 'namespace' for complete model is 'namespace' of first source
237
+ // (not a really useful property at all, avoids XSN inspection by Umbrella)
238
+ for (const first in srcDict) {
239
+ const { namespace } = srcDict[first];
240
+ if (namespace?.path)
241
+ csn.namespace = pathName( namespace.path );
242
+ break;
243
+ }
242
244
  }
243
245
  set( 'definitions', csn, model );
244
246
  if (Object.keys(model.vocabularies || {}).length > 0)
@@ -1408,6 +1410,9 @@ function addElementAsColumn( elem, cols ) {
1408
1410
  setHidden( col, '$parens', parens.length );
1409
1411
  addLocation( (parens ? parens[parens.length - 1] : elem.value.location), col );
1410
1412
  }
1413
+ else if (elem.name && !elem.name.$inferred) {
1414
+ addLocation( elem.name.location, col );
1415
+ }
1411
1416
  cols.push( extra( col, elem ) );
1412
1417
  }
1413
1418
 
@@ -12,7 +12,7 @@ const antlr4 = require('antlr4');
12
12
 
13
13
  const { CompileMessage } = require('../base/messages');
14
14
  const errorStrategy = require('./errorStrategy');
15
- const { XsnSource } = require('../compiler/classes');
15
+ const { XsnSource } = require('../compiler/xsn-model');
16
16
 
17
17
  const Parser = require('../gen/languageParser').default;
18
18
  const Lexer = require('../gen/languageLexer').default;
@@ -14,14 +14,13 @@ const locUtils = require('../base/location');
14
14
  const { parseDocComment } = require('./docCommentParser');
15
15
  const { parseMultiLineStringLiteral } = require('./multiLineStringParser');
16
16
  const {
17
- functionsWithoutParens,
18
17
  specialFunctions,
19
18
  quotedLiteralPatterns,
20
19
  } = require('../compiler/builtins');
20
+ const { functionsWithoutParens } = require('../base/builtins');
21
+ const { Location } = require('../base/location');
21
22
  const { pathName } = require('../compiler/utils');
22
- const {
23
- XsnArtifact, XsnName, CsnLocation, XsnSource,
24
- } = require('../compiler/classes');
23
+ const { XsnArtifact, XsnName, XsnSource } = require('../compiler/xsn-model');
25
24
  const { isBetaEnabled } = require('../base/model');
26
25
  const { weakLocation } = require('../base/location');
27
26
  const { normalizeNewLine, normalizeNumberString } = require('./textUtils');
@@ -500,10 +499,10 @@ function handleDuplicateExtension( ext, name, numDefines ) {
500
499
  * Return start location of `token`, or the first token matched by the current
501
500
  * rule if `token` is undefined
502
501
  *
503
- * @returns {CsnLocation}
502
+ * @returns {Location}
504
503
  */
505
504
  function startLocation( token = this._ctx.start ) {
506
- return new CsnLocation(
505
+ return new Location(
507
506
  this.filename,
508
507
  token.line,
509
508
  token.column + 1
@@ -516,7 +515,7 @@ function startLocation( token = this._ctx.start ) {
516
515
  *
517
516
  * @param {object} token
518
517
  * @param {object} endToken
519
- * @return {CsnLocation}
518
+ * @return {Location}
520
519
  */
521
520
  function tokenLocation( token, endToken = null ) {
522
521
  if (!token)
@@ -528,7 +527,7 @@ function tokenLocation( token, endToken = null ) {
528
527
  const endLine = endToken.line;
529
528
  // after the last char (special for EOF?)
530
529
  const endCol = endToken.stop - endToken.start + endToken.column + 2;
531
- const loc = new CsnLocation( this.filename, token.line, token.column + 1, endLine, endCol );
530
+ const loc = new Location( this.filename, token.line, token.column + 1, endLine, endCol );
532
531
 
533
532
  // This check is done for performance reason. No need to access a token's
534
533
  // data if we know that it spans only one single line.
@@ -629,7 +628,7 @@ function secureParens( expr ) {
629
628
  return {
630
629
  op: { val: 'xpr', location: this.startLocation() },
631
630
  args: [ expr ],
632
- location: { __proto__: CsnLocation.prototype, ...expr.location },
631
+ location: { __proto__: Location.prototype, ...expr.location },
633
632
  $parens,
634
633
  };
635
634
  }
@@ -1346,7 +1345,7 @@ function argsExpression( args, nary ) {
1346
1345
  return args[0];
1347
1346
  const $parens = args[0]?.$parens;
1348
1347
  const loc = ($parens) ? $parens[$parens.length - 1] : args[0]?.location;
1349
- const location = loc ? { __proto__: CsnLocation.prototype, ...loc } : this.startLocation();
1348
+ const location = loc ? { __proto__: Location.prototype, ...loc } : this.startLocation();
1350
1349
  // console.log('AE:',args);
1351
1350
  const op = {
1352
1351
  // eslint-disable-next-line no-nested-ternary
@@ -6,7 +6,7 @@ const {
6
6
  cdlNewLineRegEx,
7
7
  } = require('./textUtils');
8
8
  const { CompilerAssertion } = require('../base/error');
9
- const { CsnLocation } = require('../compiler/classes');
9
+ const { Location } = require('../base/location');
10
10
 
11
11
  /**
12
12
  * Strips and counts the indentation from the given string.
@@ -463,7 +463,7 @@ class MultiLineStringParser {
463
463
  */
464
464
  _locationForCharacters(i, width) {
465
465
  return {
466
- __proto__: CsnLocation.prototype,
466
+ __proto__: Location.prototype,
467
467
  file: this.parser.filename,
468
468
  line: this.token.line + this._lineInString,
469
469
  endLine: this.token.line + this._lineInString,
@@ -45,7 +45,7 @@ function isWhitespaceCharacterNoNewline( char ) {
45
45
  * @return {string}
46
46
  */
47
47
  function normalizeNewLine( str ) {
48
- // Note: cdlNewLineRegEx does not have `g` flag and we can't use replaceAll in Node 14.
48
+ // Note: cdlNewLineRegEx does not have `g`.
49
49
  return str.replace(new RegExp(cdlNewLineRegEx, 'ug'), '\n');
50
50
  }
51
51
 
package/lib/main.d.ts CHANGED
@@ -246,6 +246,15 @@ declare namespace compiler {
246
246
  * @see service
247
247
  */
248
248
  serviceNames?: string[]
249
+ /**
250
+ * If set, certain OData errors that are not relevant for OpenAPI generation
251
+ * are downgraded to warnings when generating EDM JSON.
252
+ *
253
+ * @default true
254
+ * @since v4.8.0
255
+ * @private
256
+ */
257
+ edm4OpenAPI?: boolean
249
258
  }
250
259
 
251
260
  /**
@@ -951,6 +960,20 @@ declare namespace compiler {
951
960
  */
952
961
  const keywords: string[];
953
962
  }
963
+ namespace h2 {
964
+ /**
965
+ * Immutable list of reserved keywords for H2. The list is used by {@link to.sql}.
966
+ * Taken from <http://www.h2database.com/html/advanced.html#keywords>.
967
+ */
968
+ const keywords: string[];
969
+ }
970
+ namespace postgres {
971
+ /**
972
+ * Immutable list of reserved keywords for PostgreSQL. The list is used by {@link to.sql}.
973
+ * Taken from <https://www.postgresql.org/docs/current/sql-keywords-appendix.html>.
974
+ */
975
+ const keywords: string[];
976
+ }
954
977
 
955
978
  /**
956
979
  * If the given `name` requires quoting for SQL dialect `dialect`,
package/lib/main.js CHANGED
@@ -26,7 +26,7 @@ const parseLanguage = lazyload('./language/antlrParser');
26
26
  const compiler = lazyload('./compiler');
27
27
  const shared = lazyload('./compiler/shared');
28
28
  const define = lazyload('./compiler/define');
29
- const builtins = lazyload('./compiler/builtins');
29
+ const builtins = lazyload('./base/builtins');
30
30
  const base = lazyload('./compiler/base');
31
31
  const finalizeParseCdl = lazyload('./compiler/finalize-parse-cdl');
32
32
 
@@ -126,6 +126,7 @@ module.exports = {
126
126
  // SNAPI
127
127
  for: {
128
128
  odata: (...args) => snapi.odata(...args),
129
+ java: (...args) => snapi.java(...args),
129
130
  effective: (...args) => snapi.for_effective(...args),
130
131
  },
131
132
  to: {
@@ -141,6 +142,12 @@ module.exports = {
141
142
  sqlite: {
142
143
  keywords: Object.freeze([ ...keywords.sqlite ] )
143
144
  },
145
+ postgres: {
146
+ keywords: Object.freeze([ ...keywords.postgres ] )
147
+ },
148
+ h2: {
149
+ keywords: Object.freeze([ ...keywords.h2 ] )
150
+ },
144
151
  smartId: (...args) => sqlIdentifier.smartId(...args),
145
152
  smartFunctionId: (...args) => sqlIdentifier.smartFuncId(...args),
146
153
  delimitedId: (...args) => sqlIdentifier.delimitedId(...args),
@@ -3,7 +3,7 @@
3
3
  const { csnPropertyOrder } = require('../json/to-csn');
4
4
  const { ModelError } = require('../base/error');
5
5
  const { setHidden } = require('../utils/objectUtils');
6
- const { isAnnotationExpression } = require('../compiler/builtins');
6
+ const { isAnnotationExpression } = require('../base/builtins');
7
7
 
8
8
  const csnDictionaries = [
9
9
  'args',
@@ -21,16 +21,22 @@ function shallowCopy( val, _options, _sort ) {
21
21
  }
22
22
 
23
23
  const internalCsnProps = {
24
+ __proto__: null,
24
25
  $sources: shallowCopy,
25
26
  $location: shallowCopy,
26
27
  $path: shallowCopy,
27
28
  $paths: shallowCopy,
28
29
  elements: cloneCsnDict,
29
30
  $tableConstraints: shallowCopy,
31
+ $default: shallowCopy, // used for HANA CSN migrations
32
+ $notNull: shallowCopy, // used for HANA CSN migrations
33
+ };
34
+ const internalEnumerableCsnProps = {
35
+ __proto__: null,
36
+ $tableConstraints: shallowCopy, // enumerable for HANA CSN for migrations
30
37
  };
31
38
  const internalCsnPropertyNames = Object.keys(internalCsnProps);
32
39
 
33
-
34
40
  /**
35
41
  * Deeply clone the given CSN model and return it.
36
42
  * In testMode (or with testSortCsn), definitions are sorted.
@@ -58,18 +64,21 @@ function cloneCsn( csn, options, sort ) {
58
64
  const r = {};
59
65
  for (const n of keys) {
60
66
  const val = csn[n];
61
- if (!val || typeof val !== 'object') {
62
- r[n] = val;
63
- }
64
- else if (n.charAt(0) === '@') {
67
+ if (n.charAt(0) === '@') {
65
68
  r[n] = cloneAnnotationValue(val, options, false); // TODO: pass 'sort'
66
69
  }
70
+ else if (!val || typeof val !== 'object') {
71
+ r[n] = val;
72
+ }
67
73
  else if (csnDictionaries.includes(n) && !Array.isArray(val)) {
68
74
  const sortDict = n === 'definitions' &&
69
75
  (!options || options.testMode || options.testSortCsn);
70
76
  // Array check for property `args` which may either be a dictionary or an array.
71
77
  r[n] = cloneCsnDict(val, options, sort, sortDict);
72
78
  }
79
+ else if (n in internalEnumerableCsnProps) {
80
+ r[n] = internalEnumerableCsnProps[n](val, options, sort);
81
+ }
73
82
  else {
74
83
  r[n] = cloneCsn(val, options, sort);
75
84
  }
@@ -171,15 +171,14 @@
171
171
  // hierarchy, query number, table aliases and links from a column to its
172
172
  // respective inferred element.
173
173
 
174
- // TODO: some `name` property would be useful (also set with `initDefinition`)
175
-
176
174
  // Properties in cache:
177
175
  //
178
176
  // - _effectiveType on def/member/items: cached result of effectiveType()
179
177
  // - _origin on def/member/items: the "prototype"
180
178
  // - $origin on def/member/items: whether implicit _origin refs have been set for direct members
181
179
  // - _parent: currently just use to allow ref to `up_` in anonymous aspect
182
- // for managed compositions
180
+ // for managed compositions;
181
+ // in queries always the main artifact (see `$next` for name resolution)
183
182
  // - _env on non-string path item: environment provided by the ref so far,
184
183
  // next path item is element in it
185
184
  // - _ref on non-string `type` or `from` ref, or on alias: the referred def/elem
@@ -193,10 +192,11 @@
193
192
 
194
193
  'use strict';
195
194
 
195
+
196
196
  const BUILTIN_TYPE = {};
197
- const { locationString } = require('../base/location');
197
+ const { SemanticLocation, locationString } = require('../base/location');
198
198
  const { ModelError, CompilerAssertion } = require('../base/error');
199
- const { isAnnotationExpression } = require('../compiler/builtins');
199
+ const { isAnnotationExpression } = require('../base/builtins');
200
200
 
201
201
  // Properties in which artifact or members are defined - next property in the
202
202
  // "csnPath" is the name or index of that property; 'args' (its value can be a
@@ -244,11 +244,13 @@ function justDollar() {
244
244
  * @param {boolean|string} [universalReady]
245
245
  */
246
246
  function csnRefs( csn, universalReady ) {
247
+ // some users exchange the dict while using csn-refs !?! see test/testDraft.js
248
+ // const { definitions } = csn;
247
249
  const cache = new WeakMap();
248
250
  setCache( BUILTIN_TYPE, '_origin', null );
249
251
  if (universalReady === 'init-all') {
250
- for (const art of Object.values( csn.definitions || {}))
251
- initDefinition( art );
252
+ for (const name of Object.keys( csn.definitions || {}))
253
+ initDefinition( name );
252
254
  }
253
255
  // Functions which set the new `baseEnv`:
254
256
  resolveRef.expandInline = function resolveExpandInline( ref, ...args ) {
@@ -275,6 +277,7 @@ function csnRefs( csn, universalReady ) {
275
277
  initDefinition,
276
278
  dropDefinitionCache,
277
279
  targetAspect,
280
+ msgLocations,
278
281
  __getCache_forEnrichCsnDebugging: obj => cache.get( obj ),
279
282
  };
280
283
 
@@ -324,9 +327,7 @@ function csnRefs( csn, universalReady ) {
324
327
  const target = (staticAssoc ? targetAspect( env ) : env.target || env.targetAspect);
325
328
  if (typeof target !== 'string')
326
329
  return target || env;
327
- const def = csn.definitions[target];
328
- initDefinition( def );
329
- return def;
330
+ return initDefinition( target );
330
331
  }
331
332
 
332
333
  /**
@@ -342,18 +343,19 @@ function csnRefs( csn, universalReady ) {
342
343
  if (typeof ref === 'string') {
343
344
  const main = csn.definitions[ref];
344
345
  if (main)
345
- return initDefinition( main );
346
+ return initDefinition( ref );
347
+ // notFound only meant for builtins and $self
348
+ if (notFound !== undefined)
349
+ return notFound;
346
350
  }
347
351
  else {
348
352
  const art = cached( ref, '_ref', artifactPathRef );
349
353
  if (art)
350
354
  return art;
355
+ // Backend bug workaround, TODO: delete next 2 lines
356
+ if (notFound !== undefined)
357
+ return notFound;
351
358
  }
352
- if (notFound !== undefined && typeof ref === 'string')
353
- return notFound; // is only meant for builtins and $self
354
- // Backend bug workaround, TODO: delete next 2 lines
355
- if (notFound !== undefined)
356
- return notFound;
357
359
  throw new ModelError( `Unknown artifact reference: ${ typeof ref !== 'string' ? JSON.stringify(ref.ref) : ref }` );
358
360
  }
359
361
 
@@ -367,8 +369,7 @@ function csnRefs( csn, universalReady ) {
367
369
 
368
370
  function artifactPathRef( ref ) {
369
371
  const [ head, ...tail ] = ref.ref;
370
- let art = csn.definitions[pathId( head )];
371
- initDefinition( art );
372
+ let art = initDefinition( pathId( head ) );
372
373
  for (const elem of tail) {
373
374
  const env = navigationEnv( art );
374
375
  art = env.elements[pathId( elem )];
@@ -378,8 +379,7 @@ function csnRefs( csn, universalReady ) {
378
379
 
379
380
  function artifactFromRef( ref, noLast ) {
380
381
  const [ head, ...tail ] = ref.ref;
381
- let art = csn.definitions[pathId( head )];
382
- initDefinition( art );
382
+ let art = initDefinition( pathId( head ) );
383
383
  for (const elem of tail) {
384
384
  const env = navigationEnv( art );
385
385
  art = env.elements[pathId( elem )];
@@ -401,9 +401,7 @@ function csnRefs( csn, universalReady ) {
401
401
  const targetName = refCtx !== 'keys_origin' && art.target ||
402
402
  art.$origin && art.$origin.target ||
403
403
  art.cast.target;
404
- const target = csn.definitions[targetName];
405
- initDefinition( target );
406
- return target;
404
+ return initDefinition( targetName );
407
405
  }
408
406
 
409
407
  function getOrigin( art ) {
@@ -437,9 +435,8 @@ function csnRefs( csn, universalReady ) {
437
435
  if (!Array.isArray( $origin )) // anonymous prototype in $origin
438
436
  return getOriginExplicit( $origin.$origin );
439
437
  const [ head, ...tail ] = $origin;
440
- const main = csn.definitions[head];
441
438
  // if (!main) throw Error(JSON.stringify({$origin,csn}))
442
- initDefinition( main );
439
+ const main = initDefinition( head );
443
440
  return tail.reduce( originNavigation, main );
444
441
  }
445
442
 
@@ -483,8 +480,13 @@ function csnRefs( csn, universalReady ) {
483
480
  }
484
481
 
485
482
  function initDefinition( main ) {
483
+ const name = typeof main === 'string' && main;
484
+ if (name) {
485
+ main = csn.definitions[name];
486
+ setCache( main, '$name', name );
487
+ }
486
488
  // TODO: some --test-mode check that the argument is in ‹csn›.definitions ?
487
- if (getCache( main, '$queries' ) !== undefined) // already computed
489
+ if (!main || getCache( main, '$queries' ) !== undefined) // already computed
488
490
  return main;
489
491
  traverseDef( main, null, null, null, initNode );
490
492
  const queries = cached( main, '$queries', allQueries );
@@ -650,7 +652,7 @@ function csnRefs( csn, universalReady ) {
650
652
  const up = getCache( parent, '_parent' );
651
653
  const target = up && typeof up.target === 'string' && csn.definitions[up.target];
652
654
  if (target && target.elements) {
653
- initDefinition( target );
655
+ initDefinition( up.target );
654
656
  art = target.elements.up_;
655
657
  }
656
658
  }
@@ -734,6 +736,110 @@ function csnRefs( csn, universalReady ) {
734
736
  };
735
737
  }
736
738
 
739
+ /**
740
+ * Return [ Location, SemanticLocation ] from `csnPath`.
741
+ */
742
+ function msgLocations( csnPath ) {
743
+ let location = csn?.$location;
744
+ const artifact = new SemanticLocation();
745
+ let obj = csn;
746
+ let index = 0;
747
+ let inlinePathIndex = null;
748
+ if (typeof csnPath[0] === 'object')
749
+ startPath( csnPath[0] );
750
+
751
+ /* eslint-disable no-return-assign */
752
+ const pathFunctions = {
753
+ definitions: name => absolute( name, 'type' ),
754
+ vocabularies: name => absolute( name, 'annotation' ),
755
+ extensions,
756
+ projection,
757
+ SELECT: projection,
758
+ // TODO: alias
759
+ mixin: name => nameInProp( name, 'mixin' ),
760
+ actions: name => nameInProp( name, 'action' ),
761
+ params: name => nameInProp( name, 'param' ),
762
+ returns: () => (artifact.param = ''),
763
+ elements: name => elements( name, artifact.select == null ? null : 'element' ),
764
+ columns: elements,
765
+ expand: elements,
766
+ inline: elements,
767
+ keys: pos => elements( pos, 'key' ),
768
+ enum: name => elements( name, 'enum' ),
769
+ item: () => (artifact.innerKind = 'item'),
770
+ // targetAspect: () => (artifact.innerKind = 'aspect')
771
+ '@': suffix,
772
+ }; /* eslint-enable no-return-assign */
773
+
774
+ while (obj && index < csnPath.length) {
775
+ const step = csnPath[index++];
776
+ obj = obj[step];
777
+ const fn = pathFunctions[step] || pathFunctions[step.charAt( 0 )];
778
+ if (fn)
779
+ fn( csnPath[index] );
780
+ if (obj?.$location)
781
+ location = obj.$location;
782
+ }
783
+ return [ location, artifact ];
784
+
785
+ function startPath( art ) {
786
+ const parent = getCache( art, '_parent' );
787
+ if (parent) {
788
+ if (!art.SELECT && !art.projection)
789
+ throw new CompilerAssertion( 'CSN path starts with object other than def or query' );
790
+ }
791
+ obj = csn.definitions;
792
+ absolute( getCache( parent || art, '$name' ), 'type' );
793
+ obj = art;
794
+ location = art.$location || parent?.$location || csn.$location;
795
+ }
796
+
797
+ function absolute( name, defaultKind ) {
798
+ obj = obj[name];
799
+ artifact.mainKind = obj.kind || defaultKind;
800
+ artifact.absolute = name;
801
+ ++index;
802
+ }
803
+ function extensions( pos ) {
804
+ obj = obj[pos];
805
+ artifact.mainKind = obj.annotate ? 'annotate' : 'extend';
806
+ artifact.absolute = obj.annotate || obj.extend;
807
+ ++index;
808
+ }
809
+ function projection() {
810
+ let select = getCache( obj, '$queryNumber' );
811
+ if (select === 1) {
812
+ const parent = getCache( obj, '_parent' );
813
+ if (parent && getCache( parent, '$queries' )?.length === 1)
814
+ select = 0;
815
+ }
816
+ artifact.select = select;
817
+ }
818
+ function nameInProp( name, prop ) {
819
+ obj = obj[name];
820
+ artifact[prop] = name;
821
+ ++index;
822
+ }
823
+ function elements( name, kind ) {
824
+ obj = obj[name];
825
+ const elem = (typeof name === 'string') ? name : !obj.inline && columnAlias( obj );
826
+ if (obj.inline) { // inline
827
+ inlinePathIndex ??= artifact.element.length;
828
+ }
829
+ else if (inlinePathIndex != null) { // inline before: remove inline col indexes
830
+ if (elem)
831
+ artifact.element.length = inlinePathIndex;
832
+ inlinePathIndex = null;
833
+ }
834
+ artifact.element.push( elem || name + 1);
835
+ artifact.innerKind = kind || undefined;
836
+ ++index;
837
+ }
838
+ function suffix( prop ) {
839
+ artifact.suffix = prop;
840
+ obj = null; // stop
841
+ }
842
+ }
737
843
  /**
738
844
  * Get the array of all (sub-)queries (value of the `SELECT`/`projection`
739
845
  * property) inside the given `main` artifact (of `main.query`).
@@ -1021,16 +1127,18 @@ function implicitAs( ref ) {
1021
1127
  function startCsnPath( csnPath, csn ) {
1022
1128
  const head = csnPath[0];
1023
1129
  if (typeof head !== 'string') {
1024
- const { main, parent, art } = head;
1130
+ const {
1131
+ main, parent, art, query,
1132
+ } = head;
1025
1133
  return {
1026
- index: 1, main, parent, art,
1134
+ index: 1, main, parent, art, query,
1027
1135
  };
1028
1136
  }
1029
- if (csnPath.length < 2 || csnPath[0] !== 'definitions' && csnPath[0] !== 'vocabularies')
1137
+ if (csnPath.length < 2 || head !== 'definitions' && head !== 'vocabularies')
1030
1138
  throw new CompilerAssertion( 'References outside definitions and vocabularies not supported yet');
1031
- const art = csn[csnPath[0]][csnPath[1]];
1139
+ const art = csn[head][csnPath[1]];
1032
1140
  return {
1033
- index: 2, main: art, parent: art, art,
1141
+ index: 2, main: art, parent: art, art, query: null,
1034
1142
  };
1035
1143
  }
1036
1144
 
@@ -1041,8 +1149,6 @@ function startCsnPath( csnPath, csn ) {
1041
1149
  * @param {any} resolve
1042
1150
  */
1043
1151
  function analyseCsnPath( csnPath, csn, resolve ) {
1044
- /** @type {object} */
1045
- let query = null;
1046
1152
  /** @type {any} */
1047
1153
  let refCtx = null;
1048
1154
  /** @type {boolean|string|number} */
@@ -1051,7 +1157,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
1051
1157
  let baseCtx = null;
1052
1158
  let baseEnv = null;
1053
1159
  let {
1054
- index, main, parent, art,
1160
+ index, main, parent, art, query,
1055
1161
  } = startCsnPath( csnPath, csn );
1056
1162
  let obj = art;
1057
1163
 
@@ -7,7 +7,7 @@ const {
7
7
  applyTransformationsOnNonDictionary,
8
8
  applyTransformationsOnDictionary,
9
9
  } = require('../transform/db/applyTransformations');
10
- const { isBuiltinType, isMagicVariable, isAnnotationExpression } = require('../compiler/builtins.js');
10
+ const { isBuiltinType, isAnnotationExpression } = require('../base/builtins');
11
11
  const { ModelError, CompilerAssertion } = require('../base/error');
12
12
  const { typeParameters } = require('../compiler/builtins');
13
13
  const { forEach } = require('../utils/objectUtils');
@@ -1414,9 +1414,6 @@ function findAnnotationExpression( node, prop ) {
1414
1414
 
1415
1415
  module.exports = {
1416
1416
  getUtils,
1417
- isBuiltinType,
1418
- isMagicVariable,
1419
- isAnnotationExpression,
1420
1417
  applyAnnotationsFromExtensions,
1421
1418
  forEachGeneric,
1422
1419
  forEachDefinition,