@sap/cds-compiler 4.0.0 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +115 -5
  2. package/bin/cdsc.js +12 -12
  3. package/doc/CHANGELOG_BETA.md +11 -0
  4. package/lib/api/main.js +60 -12
  5. package/lib/api/validate.js +1 -1
  6. package/lib/base/location.js +6 -7
  7. package/lib/base/message-registry.js +84 -38
  8. package/lib/base/messages.js +11 -10
  9. package/lib/base/model.js +6 -2
  10. package/lib/checks/defaultValues.js +6 -6
  11. package/lib/checks/foreignKeys.js +0 -5
  12. package/lib/checks/onConditions.js +17 -12
  13. package/lib/checks/queryNoDbArtifacts.js +132 -72
  14. package/lib/checks/sql-snippets.js +15 -4
  15. package/lib/checks/types.js +3 -3
  16. package/lib/checks/utils.js +1 -1
  17. package/lib/compiler/assert-consistency.js +44 -16
  18. package/lib/compiler/base.js +1 -0
  19. package/lib/compiler/builtins.js +7 -8
  20. package/lib/compiler/checks.js +274 -197
  21. package/lib/compiler/classes.js +62 -0
  22. package/lib/compiler/cycle-detector.js +3 -3
  23. package/lib/compiler/define.js +63 -50
  24. package/lib/compiler/extend.js +38 -20
  25. package/lib/compiler/finalize-parse-cdl.js +2 -1
  26. package/lib/compiler/generate.js +0 -8
  27. package/lib/compiler/index.js +9 -7
  28. package/lib/compiler/kick-start.js +2 -0
  29. package/lib/compiler/populate.js +139 -110
  30. package/lib/compiler/propagator.js +4 -3
  31. package/lib/compiler/resolve.js +157 -126
  32. package/lib/compiler/shared.js +706 -404
  33. package/lib/compiler/tweak-assocs.js +21 -10
  34. package/lib/compiler/utils.js +228 -36
  35. package/lib/edm/annotations/genericTranslation.js +30 -2
  36. package/lib/edm/edm.js +4 -1
  37. package/lib/edm/edmPreprocessor.js +12 -5
  38. package/lib/edm/edmUtils.js +2 -4
  39. package/lib/gen/Dictionary.json +34 -10
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +3987 -3963
  43. package/lib/json/from-csn.js +43 -47
  44. package/lib/json/to-csn.js +11 -11
  45. package/lib/language/antlrParser.js +2 -1
  46. package/lib/language/genericAntlrParser.js +52 -43
  47. package/lib/language/language.g4 +59 -59
  48. package/lib/language/multiLineStringParser.js +2 -0
  49. package/lib/main.d.ts +5 -0
  50. package/lib/model/csnRefs.js +37 -19
  51. package/lib/model/csnUtils.js +20 -16
  52. package/lib/model/revealInternalProperties.js +29 -21
  53. package/lib/model/sortViews.js +4 -2
  54. package/lib/modelCompare/compare.js +112 -39
  55. package/lib/modelCompare/utils/filter.js +54 -24
  56. package/lib/optionProcessor.js +6 -6
  57. package/lib/render/manageConstraints.js +20 -17
  58. package/lib/render/toCdl.js +34 -20
  59. package/lib/render/toHdbcds.js +2 -2
  60. package/lib/render/toRename.js +4 -9
  61. package/lib/render/toSql.js +77 -26
  62. package/lib/render/utils/common.js +3 -3
  63. package/lib/render/utils/unique.js +52 -0
  64. package/lib/transform/db/applyTransformations.js +61 -20
  65. package/lib/transform/db/assertUnique.js +7 -8
  66. package/lib/transform/db/associations.js +2 -2
  67. package/lib/transform/db/cdsPersistence.js +8 -8
  68. package/lib/transform/db/expansion.js +17 -21
  69. package/lib/transform/db/flattening.js +23 -23
  70. package/lib/transform/db/rewriteCalculatedElements.js +20 -14
  71. package/lib/transform/db/temporal.js +1 -1
  72. package/lib/transform/db/transformExists.js +8 -7
  73. package/lib/transform/db/views.js +73 -33
  74. package/lib/transform/draft/db.js +11 -9
  75. package/lib/transform/draft/odata.js +1 -1
  76. package/lib/transform/{forOdataNew.js → forOdata.js} +56 -42
  77. package/lib/transform/forRelationalDB.js +69 -75
  78. package/lib/transform/localized.js +6 -5
  79. package/lib/transform/odata/toFinalBaseType.js +3 -3
  80. package/lib/transform/{transformUtilsNew.js → transformUtils.js} +4 -101
  81. package/lib/transform/translateAssocsToJoins.js +14 -28
  82. package/package.json +1 -1
  83. package/share/messages/check-proper-type-of.md +1 -1
  84. package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
  85. package/share/messages/message-explanations.json +1 -1
@@ -84,6 +84,7 @@
84
84
  * @property {string[]} [xorGroups]
85
85
  * Corresponding xor groups. It references a value of xorGroups. If set then only one property
86
86
  * of the xorGroup may be set, e.g. if target is set, elements may not.
87
+ * If you are looking for a `notWith` (which should be symmetric), this is your property.
87
88
  *
88
89
  * @property {string} [xsnOp]
89
90
  * Defines the operator to be used for XSN. Used for SET and SELECT. See queryTerm().
@@ -117,6 +118,7 @@
117
118
  const { dictAdd } = require('../base/dictionaries');
118
119
  const { quotedLiteralPatterns } = require('../compiler/builtins');
119
120
  const { CompilerAssertion } = require('../base/error');
121
+ const { XsnSource, CsnLocation } = require('../compiler/classes');
120
122
 
121
123
  const $location = Symbol.for('cds.$location');
122
124
 
@@ -144,20 +146,22 @@ const exprProperties = [
144
146
  // Groups of properties which cannot be used together:
145
147
  const xorGroups = {
146
148
  // include CSN v0.1.0 properties here:
147
- ':type': [ 'target', 'elements', 'enum', 'items' ],
149
+ ':type': [
150
+ 'target', 'targetAspect', 'elements', 'items', // xorException: target+targetAspect
151
+ 'length', 'precision', 'scale', 'srid', // xorException: precision+scale
152
+ ],
153
+ ':enum': [ 'target', 'targetAspect', 'elements', 'enum', 'items' ],
148
154
  ':expr': [ // see also xorException property in schema
149
155
  'ref', 'xpr', 'list', 'val', '#', 'func', 'SELECT', 'SET', 'expand',
150
156
  '=', 'path', 'value', 'op', // '='/'path' is CSN v0.1.0 here
151
157
  ],
152
158
  ':col': [ 'expand', 'inline' ],
153
159
  ':ext': [ 'annotate', 'extend' ], // TODO: better msg for test/negative/UnexpectedProperties.csn
154
- ':assoc': [ 'on', 'keys', 'foreignKeys', 'onCond' ], // 'foreignKeys'/'onCond' is CSN v0.1.0
155
-
156
- // TODO: Maybe use a field "notWith", similar to "onlyWith" on `elements`/`items`?
157
- ':length': [ 'elements', 'items', 'length' ],
158
- ':srid': [ 'elements', 'items', 'srid' ],
159
- ':precision': [ 'elements', 'items', 'precision' ],
160
- ':scale': [ 'elements', 'items', 'scale' ],
160
+ ':assoc': [
161
+ 'on', 'keys',
162
+ 'foreignKeys', 'onCond', // 'foreignKeys'/'onCond' is CSN v0.1.0
163
+ ],
164
+ ':on': [ 'on', 'default' ],
161
165
 
162
166
  // TODO - improve consequential errors: assume no name given with `join` or `inline`?
163
167
  as: [ 'as', 'join', 'inline' ],
@@ -272,13 +276,6 @@ const schema = compileSchema( {
272
276
  'extend',
273
277
  ],
274
278
  },
275
- payload: { // keep it for a while, TODO: remove with v2 - at least warning
276
- dictionaryOf: definition, // duplicate of line below only for better error message
277
- type: renameTo( 'elements', dictionaryOf( definition ) ),
278
- defaultKind: 'element',
279
- validKinds: [],
280
- inKind: [ 'event' ],
281
- },
282
279
  actions: {
283
280
  dictionaryOf: actions,
284
281
  defaultKind: 'action',
@@ -364,6 +361,7 @@ const schema = compileSchema( {
364
361
  },
365
362
  targetAspect: {
366
363
  type: artifactRef,
364
+ xorException: 'target', // see xorGroup :type
367
365
  msgVariant: 'or-object', // for 'syntax-expecting-string',
368
366
  requires: 'elements',
369
367
  optional: [ 'elements' ], // 'elements' for ad-hoc aspect compositions
@@ -371,6 +369,7 @@ const schema = compileSchema( {
371
369
  },
372
370
  target: {
373
371
  type: artifactRef,
372
+ xorException: 'targetAspect', // see xorGroup :type
374
373
  msgVariant: 'or-object', // for 'syntax-expecting-string',
375
374
  requires: 'elements',
376
375
  optional: [ 'elements' ], // 'elements' for ad-hoc COMPOSITION OF (gensrc style CSN)
@@ -398,10 +397,12 @@ const schema = compileSchema( {
398
397
  },
399
398
  precision: {
400
399
  type: natnum,
400
+ xorException: 'scale', // see xorGroup :type
401
401
  inKind: [ 'element', 'type', 'param', 'annotation', 'extend' ],
402
402
  },
403
403
  scale: {
404
404
  type: scalenum,
405
+ xorException: 'precision', // see xorGroup :type
405
406
  inKind: [ 'element', 'type', 'param', 'annotation', 'extend' ],
406
407
  },
407
408
  srid: {
@@ -990,15 +991,12 @@ function definition( def, spec, xsn, csn, name ) {
990
991
  const kind0 = (spec.validKinds.length || spec.prop === 'extensions') && kind;
991
992
  const csnProps = Object.keys( def );
992
993
 
993
- // If we have `extend … with { name = 3 }`, it could mean adding an element or an enum.
994
- // We use `elements: { name: { val: 3 } }` to represent this ambiguity.
995
- if (savedInExtensions === '' && prop === 'elements' &&
996
- (def.val !== undefined || def['#'] !== undefined ||
997
- def.value?.val !== undefined || def.value?.['#'] !== undefined) &&
998
- def.virtual === undefined && def.key === undefined && def.masked === undefined &&
999
- def.type === undefined && def.elements === undefined) {
1000
- r.$syntax = 'enum';
1001
- kind = 'enum'; // for function expected()
994
+ // For compatibility, extension property `elements` could actually be an `enum`:
995
+ if (savedInExtensions === '' && prop === 'elements' && // in extend property `elements`
996
+ !Object.keys( def ).some( couldNotBeEnumProperty )) {
997
+ r.$syntax = 'enum'; // could be an enum
998
+ if (def.val !== undefined || def['#'] !== undefined)
999
+ kind = 'enum'; // for function expected(), i.e. allow property `val`/`#`
1002
1000
  }
1003
1001
 
1004
1002
  if (csnProps.length) {
@@ -1018,15 +1016,6 @@ function definition( def, spec, xsn, csn, name ) {
1018
1016
  r.name = { id: name, location: r.location };
1019
1017
  if (prop === 'columns' || prop === 'keys' || prop === 'foreignKeys')
1020
1018
  r.name.$inferred = 'as';
1021
- // TODO the following 'if' (if necessary) should be part of the core compiler
1022
- if (prop === 'definitions' || prop === 'vocabularies') { // as spec property
1023
- r.name = {
1024
- absolute: name,
1025
- id: name.substring( name.lastIndexOf('.') + 1 ),
1026
- path: [ { id: name, location: r.location } ],
1027
- location: r.location,
1028
- };
1029
- }
1030
1019
  }
1031
1020
  if (spec.requires)
1032
1021
  onlyWith( spec, spec.requires, def, null, xor, () => true );
@@ -1054,6 +1043,13 @@ function definition( def, spec, xsn, csn, name ) {
1054
1043
  }
1055
1044
  }
1056
1045
 
1046
+ function couldNotBeEnumProperty( prop ) {
1047
+ // returns true for `value` (which we allow with warning when extending an enum with `elements`)
1048
+ const inKind = schema[prop]?.inKind; // undefined for annotations, $location, …
1049
+ // inKind for annotation assignments is function -> can be for enum
1050
+ return Array.isArray( inKind ) && inKind.includes( 'element' );
1051
+ }
1052
+
1057
1053
  function actions( def, spec, xsn, csn, name ) {
1058
1054
  if (def.kind === 'extend' && (def.elements || def.enum)) {
1059
1055
  // TODO: Handle this case in extend.js; already done for `returns`
@@ -1472,7 +1468,7 @@ function annoValue( val, spec ) {
1472
1468
  function annotation( val, spec, xsn, csn, name ) {
1473
1469
  const absolute = (xsn ? name.substring(1) : name);
1474
1470
  // TODO: really care about variant (qualifier parts)?
1475
- const variantIndex = absolute.indexOf('#') + 1 || absolute.length;
1471
+ const variantIndex = absolute.indexOf('#') + 1 || absolute.length; // including '#'
1476
1472
  const n = refSplit( absolute.substring( 0, variantIndex ), !xsn && '{}' );
1477
1473
  if (!n)
1478
1474
  return undefined;
@@ -1941,7 +1937,8 @@ function replaceZeroValue( spec, msgVariant, newValue ) {
1941
1937
  */
1942
1938
  function location( enforceJsonPos ) {
1943
1939
  return !enforceJsonPos && dollarLocations.length &&
1944
- dollarLocations[dollarLocations.length - 1] || {
1940
+ dollarLocations[dollarLocations.length - 1] || {
1941
+ __proto__: CsnLocation.prototype,
1945
1942
  file: csnFilename,
1946
1943
  line: virtualLine,
1947
1944
  col: 0,
@@ -1954,7 +1951,7 @@ function pushLocation( obj ) {
1954
1951
  if (loc === undefined)
1955
1952
  return;
1956
1953
  if (loc && typeof loc === 'object' && !Array.isArray( loc )) {
1957
- dollarLocations.push( loc.line ? loc : null );
1954
+ dollarLocations.push( loc.line ? { __proto__: CsnLocation.prototype, ...loc } : null );
1958
1955
  return;
1959
1956
  }
1960
1957
  else if (!loc || typeof loc !== 'string') {
@@ -1970,10 +1967,9 @@ function pushLocation( obj ) {
1970
1967
  else {
1971
1968
  const line = Number( m[1] );
1972
1969
  const column = m[2] && Number( m[2] ) || 0;
1973
- dollarLocations.push( {
1974
- file: loc.substring( 0, m.index ),
1975
- line,
1976
- col: column,
1970
+ const file = loc.substring( 0, m.index );
1971
+ dollarLocations.push({
1972
+ __proto__: CsnLocation.prototype, file, line, col: column,
1977
1973
  } );
1978
1974
  }
1979
1975
  }
@@ -2011,7 +2007,8 @@ function toXsn( csn, filename, options, messageFunctions ) {
2011
2007
  inExtensions = null;
2012
2008
  vocabInDefinitions = null;
2013
2009
 
2014
- const xsn = { $frontend: 'json' };
2010
+ const xsn = new XsnSource();
2011
+ xsn.$frontend = 'json';
2015
2012
 
2016
2013
  // eslint-disable-next-line object-curly-newline
2017
2014
  ({ message, error, warning, info } = messageFunctions);
@@ -2050,7 +2047,7 @@ function parse( source, filename = 'csn.json', options = {}, messageFunctions =
2050
2047
  resetHeapModuleVars();
2051
2048
  if (!(e instanceof SyntaxError))
2052
2049
  throw e;
2053
- const xsn = {};
2050
+ const xsn = new XsnSource();
2054
2051
  const msg = e.message;
2055
2052
  const p = /in JSON at position ([0-9]+)/.exec( msg );
2056
2053
  let line = 1;
@@ -2067,12 +2064,11 @@ function parse( source, filename = 'csn.json', options = {}, messageFunctions =
2067
2064
  }
2068
2065
  column = end - eol + 1;
2069
2066
  }
2070
- /** @type {CSN.Location} */
2071
- const loc = {
2072
- file: filename,
2067
+ const loc = new CsnLocation(
2068
+ filename,
2073
2069
  line,
2074
- col: column,
2075
- };
2070
+ column
2071
+ );
2076
2072
  messageFunctions.error( 'syntax-invalid-json', loc, { msg },
2077
2073
  'Invalid JSON: $(MSG)' );
2078
2074
  return xsn;
@@ -335,7 +335,8 @@ function compactModel( model, options = model.options || {} ) {
335
335
  break;
336
336
  }
337
337
  set( 'definitions', csn, model );
338
- set( 'vocabularies', csn, model );
338
+ if (Object.keys(model.vocabularies || {}).length > 0)
339
+ set( 'vocabularies', csn, model );
339
340
  const exts = extensions( model.extensions || [], csn, model );
340
341
  if (exts && exts.length)
341
342
  csn.extensions = exts;
@@ -493,10 +494,12 @@ function standard( node ) {
493
494
  // XSN input node, not the CSN result node. Not really an issue...
494
495
  const keys = Object.keys( node ).sort( compareProperties );
495
496
  for (const prop of keys) {
496
- const transformer = transformers[prop] || transformers[prop.charAt(0)] || unexpected;
497
- const sub = transformer( node[prop], csn, node, prop );
498
- if (sub !== undefined)
499
- csn[prop] = sub;
497
+ if (node[prop] !== undefined) {
498
+ const transformer = transformers[prop] || transformers[prop.charAt(0)] || unexpected;
499
+ const sub = transformer( node[prop], csn, node, prop );
500
+ if (sub !== undefined)
501
+ csn[prop] = sub;
502
+ }
500
503
  }
501
504
  return csn;
502
505
  }
@@ -715,14 +718,12 @@ function actions( dict, _csn, node ) {
715
718
  const keys = Object.keys( dict );
716
719
  if (strictMode && node.kind === 'annotate')
717
720
  keys.sort(); // TODO: always sort with --test-mode ?
718
- return (keys.length)
719
- ? dictionary( dict, keys, 'actions' )
720
- : undefined;
721
+ return dictionary( dict, keys, 'actions' );
721
722
  }
722
723
 
723
724
  function params( dict ) {
724
725
  const keys = Object.keys( dict );
725
- return (keys.length)
726
+ return (keys.length) // TODO: still?
726
727
  ? insertOrderDict( dict )
727
728
  : undefined;
728
729
  }
@@ -1212,8 +1213,7 @@ function value( node ) {
1212
1213
  function enumValueOrCalc( v, csn, node ) {
1213
1214
  if (v.$inferred && (universalCsn || gensrcFlavor))
1214
1215
  return undefined;
1215
- // Enums can have values but if enums are extended, their kind is 'element'.
1216
- // In v4, we don't check `node.$syntax === 'enum'` anymore.
1216
+ // Enums values in CSN are without outer `value: { … }`:
1217
1217
  if (node.kind === 'enum') {
1218
1218
  Object.assign( csn, expression( v ) );
1219
1219
  }
@@ -12,6 +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
16
 
16
17
  const Parser = require('../gen/languageParser').default;
17
18
  const Lexer = require('../gen/languageLexer').default;
@@ -167,7 +168,7 @@ function parse( source, filename = '<undefined>.cds',
167
168
  throw e;
168
169
  }
169
170
  }
170
- const ast = tree && tree[rulespec.returns] || {};
171
+ const ast = tree && tree[rulespec.returns] || new XsnSource();
171
172
  ast.options = options;
172
173
  if (rulespec.$frontend)
173
174
  ast.$frontend = rulespec.$frontend;
@@ -18,6 +18,9 @@ const {
18
18
  quotedLiteralPatterns,
19
19
  } = require('../compiler/builtins');
20
20
  const { pathName } = require('../compiler/utils');
21
+ const {
22
+ XsnArtifact, XsnName, CsnLocation, XsnSource,
23
+ } = require('../compiler/classes');
21
24
  const { isBetaEnabled } = require('../base/model');
22
25
  const { weakLocation } = require('../base/messages');
23
26
  const { normalizeNewLine } = require('./textUtils');
@@ -110,7 +113,6 @@ Object.assign(GenericAntlrParser.prototype, {
110
113
  addDef,
111
114
  addItem,
112
115
  addExtension,
113
- aspectWithoutElements,
114
116
  createSource,
115
117
  createDict,
116
118
  createArray,
@@ -139,6 +141,8 @@ Object.assign(GenericAntlrParser.prototype, {
139
141
  prepareGenericKeywords,
140
142
  reportErrorForGenericKeyword,
141
143
  parseMultiLineStringLiteral,
144
+ XsnArtifact,
145
+ XsnName,
142
146
  });
143
147
 
144
148
  // Use the following function for language constructs which we (currently)
@@ -160,7 +164,13 @@ function csnParseOnly( msgId, tokens, textArgs ) {
160
164
  this.error( msgId, loc, textArgs );
161
165
  }
162
166
 
163
- /** @this {object} */
167
+ /**
168
+ * Do not propose a `;` or closing brace `}` at this position.
169
+ *
170
+ * Attention: May conflict with excludeExpected()!
171
+ *
172
+ * @this {object}
173
+ * */
164
174
  function noSemicolonHere() {
165
175
  const handler = this._errHandler;
166
176
  const t = this.getCurrentToken();
@@ -173,10 +183,21 @@ function noSemicolonHere() {
173
183
  handler.reportIgnoredWith( this, t );
174
184
  }
175
185
 
176
- // Using this function "during ATN decision making" has no effect
177
- // In front of an ATN decision, you might specify dedicated excludes
178
- // for non-LA1 tokens via a sub-array in excludes[0].
179
- // TODO: consider $nextTokens…, see commented use in rule `elementProperties`
186
+ /**
187
+ * Using this function "during ATN decision making" has no effect
188
+ * In front of an ATN decision, you might specify dedicated excludes
189
+ * for non-LA1 tokens via a sub-array in excludes[0].
190
+ * TODO: consider $nextTokens…, see commented use in rule `elementProperties`
191
+ *
192
+ * Usage Note:
193
+ * Must be used at all positions where sync() is called in the generated coding.
194
+ * ```antlr4
195
+ * { this.excludeExpected(['ACTIONS']); }
196
+ * ( WITH { this.excludeExpected(['ACTIONS']); } )?
197
+ * annotationAssignment_ll1[ $art ]* { this.excludeExpected(['ACTIONS']); }
198
+ * ACTIONS
199
+ * ```
200
+ */
180
201
  function excludeExpected( excludes ) {
181
202
  if (excludes) {
182
203
  // @ts-ignore
@@ -414,6 +435,12 @@ function checkExtensionDict( dict ) {
414
435
  def[prop] = dup[prop]; // continuation semantics: last wins
415
436
  }
416
437
  }
438
+ if (dup.$annotations) { // update deprecated $annotations for cds-lsp / annotation modeler
439
+ if (def.$annotations)
440
+ def.$annotations.push( ...dup.$annotations );
441
+ else
442
+ def.$annotations = dup.$annotations;
443
+ }
417
444
  }
418
445
  def.$duplicates = null;
419
446
  }
@@ -445,14 +472,14 @@ function handleDuplicateExtension( ext, name, numDefines ) {
445
472
  * Return start location of `token`, or the first token matched by the current
446
473
  * rule if `token` is undefined
447
474
  *
448
- * @returns {CSN.Location}
475
+ * @returns {CsnLocation}
449
476
  */
450
477
  function startLocation( token = this._ctx.start ) {
451
- return {
452
- file: this.filename,
453
- line: token.line,
454
- col: token.column + 1,
455
- };
478
+ return new CsnLocation(
479
+ this.filename,
480
+ token.line,
481
+ token.column + 1
482
+ );
456
483
  }
457
484
 
458
485
  /**
@@ -461,7 +488,7 @@ function startLocation( token = this._ctx.start ) {
461
488
  *
462
489
  * @param {object} token
463
490
  * @param {object} endToken
464
- * @return {CSN.Location}
491
+ * @return {CsnLocation}
465
492
  */
466
493
  function tokenLocation( token, endToken = null ) {
467
494
  if (!token)
@@ -469,16 +496,11 @@ function tokenLocation( token, endToken = null ) {
469
496
  if (!endToken) // including null
470
497
  endToken = token;
471
498
 
472
- /** @type {CSN.Location} */
473
- const loc = {
474
- file: this.filename,
475
- line: token.line,
476
- col: token.column + 1,
477
- // Default for single line tokens
478
- endLine: endToken.line,
479
- // after the last char (special for EOF?)
480
- endCol: endToken.stop - endToken.start + endToken.column + 2,
481
- };
499
+ // Default for single line tokens
500
+ const endLine = endToken.line;
501
+ // after the last char (special for EOF?)
502
+ const endCol = endToken.stop - endToken.start + endToken.column + 2;
503
+ const loc = new CsnLocation( this.filename, token.line, token.column + 1, endLine, endCol );
482
504
 
483
505
  // This check is done for performance reason. No need to access a token's
484
506
  // data if we know that it spans only one single line.
@@ -579,7 +601,7 @@ function secureParens( expr ) {
579
601
  return {
580
602
  op: { val: 'xpr', location: this.startLocation() },
581
603
  args: [ expr ],
582
- location: { ...expr.location },
604
+ location: { __proto__: CsnLocation.prototype, ...expr.location },
583
605
  $parens,
584
606
  };
585
607
  }
@@ -733,7 +755,11 @@ function argsExpression( args, nary, location ) {
733
755
  val, // there is no n-ary in rule conditionTerm
734
756
  location: this.startLocation(),
735
757
  };
736
- return this.attachLocation( { op, args, location: location && { ...location } } );
758
+ return this.attachLocation( {
759
+ op,
760
+ args,
761
+ location: location && { __proto__: CsnLocation.prototype, ...location },
762
+ } );
737
763
  }
738
764
 
739
765
  function pushXprToken( args ) {
@@ -1097,16 +1123,6 @@ function addExtension( ext, parent, kind, artName, elemPath ) {
1097
1123
  }
1098
1124
  }
1099
1125
 
1100
- function aspectWithoutElements( art ) {
1101
- // Empty dictionary to allow element extensions. NO, please NO empty dict.
1102
- // TODO: Checking it here does not prevent aspect in CSN input having no elements!
1103
- art.elements = this.createDict();
1104
- if (!isBetaEnabled( this.options, 'aspectWithoutElements' )) {
1105
- this.error( null, [ art.name.location, null ], {},
1106
- 'Aspects without elements are not supported, yet' );
1107
- }
1108
- }
1109
-
1110
1126
  // must be in action directly after having parsed '{', '(`, or a keyword before
1111
1127
  function createDict() {
1112
1128
  const dict = Object.create(null);
@@ -1132,14 +1148,7 @@ function finalizeDictOrArray( dict ) {
1132
1148
  }
1133
1149
 
1134
1150
  function createSource() {
1135
- return {
1136
- kind: 'source',
1137
- usings: [],
1138
- dependencies: [],
1139
- artifacts: Object.create(null),
1140
- // vocabularies: Object.create(null),
1141
- extensions: [],
1142
- };
1151
+ return new XsnSource();
1143
1152
  }
1144
1153
 
1145
1154
  // Create AST node for prefix operator `op` and arguments `args`