@sap/cds-compiler 3.0.0 → 3.0.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 (38) hide show
  1. package/CHANGELOG.md +29 -9
  2. package/bin/cdsc.js +9 -16
  3. package/lib/api/main.js +92 -40
  4. package/lib/base/keywords.js +64 -1
  5. package/lib/base/message-registry.js +17 -1
  6. package/lib/base/messages.js +38 -28
  7. package/lib/base/optionProcessorHelper.js +53 -21
  8. package/lib/compiler/assert-consistency.js +1 -1
  9. package/lib/compiler/builtins.js +40 -1
  10. package/lib/compiler/define.js +4 -2
  11. package/lib/compiler/extend.js +4 -1
  12. package/lib/compiler/populate.js +3 -1
  13. package/lib/compiler/resolve.js +1 -4
  14. package/lib/compiler/shared.js +9 -0
  15. package/lib/compiler/utils.js +2 -2
  16. package/lib/edm/annotations/preprocessAnnotations.js +10 -11
  17. package/lib/edm/csn2edm.js +15 -14
  18. package/lib/edm/edm.js +13 -12
  19. package/lib/edm/edmPreprocessor.js +30 -33
  20. package/lib/edm/edmUtils.js +3 -39
  21. package/lib/gen/language.checksum +1 -1
  22. package/lib/gen/language.interp +1 -1
  23. package/lib/gen/languageParser.js +3311 -3289
  24. package/lib/json/from-csn.js +17 -19
  25. package/lib/json/to-csn.js +3 -2
  26. package/lib/language/genericAntlrParser.js +42 -42
  27. package/lib/language/language.g4 +28 -17
  28. package/lib/model/csnRefs.js +1 -0
  29. package/lib/model/csnUtils.js +19 -8
  30. package/lib/model/revealInternalProperties.js +4 -1
  31. package/lib/optionProcessor.js +54 -38
  32. package/lib/render/toHdbcds.js +1 -1
  33. package/lib/render/toSql.js +7 -3
  34. package/lib/transform/forOdataNew.js +3 -3
  35. package/lib/transform/localized.js +15 -11
  36. package/lib/utils/file.js +28 -18
  37. package/package.json +2 -3
  38. package/share/messages/syntax-expected-integer.md +9 -8
@@ -88,6 +88,7 @@
88
88
  */
89
89
 
90
90
  const { dictAdd } = require('../base/dictionaries');
91
+ const { quotedLiteralPatterns } = require('../compiler/builtins');
91
92
 
92
93
  const $location = Symbol.for('cds.$location');
93
94
 
@@ -682,15 +683,6 @@ const topLevelSpec = {
682
683
  schema,
683
684
  };
684
685
 
685
- const validLiteralsExtra = Object.assign( Object.create(null), {
686
- // TODO: should we use quotedLiteralPatterns from genericAntlrParser?
687
- number: 'string',
688
- x: 'string',
689
- time: 'string',
690
- date: 'string',
691
- timestamp: 'string',
692
- } );
693
-
694
686
  // Module variables, schema compilation, and functors ------------------------
695
687
 
696
688
  /** @type {(id, location, textOrArguments, texts?) => void} */
@@ -1288,16 +1280,22 @@ function value( val, spec, xsn ) { // for CSN property 'val'
1288
1280
  return ignore( val );
1289
1281
  }
1290
1282
 
1291
- function literal( val, spec, xsn, csn ) {
1283
+ function literal( lit, spec, xsn, csn ) {
1292
1284
  // TODO: general: requires other property (here: 'val')
1293
1285
  const type = (csn.val == null) ? 'null' : typeof csn.val;
1294
- if (val === type) // also for 'object' which is an error for 'val'
1295
- return val;
1296
- if (typeof val === 'string' && validLiteralsExtra[val] === type)
1297
- return val;
1286
+ if (lit === type) // also for 'object' which is an error for 'val'
1287
+ return lit;
1288
+ if (typeof lit === 'string' && quotedLiteralPatterns[lit]?.json_type === type) {
1289
+ const p = quotedLiteralPatterns[lit];
1290
+ if (p && (p.test_fn && !p.test_fn(csn.val) || p.test_re && !p.test_re.test(csn.val)))
1291
+ warning( 'syntax-invalid-literal', location(), { '#': p.test_variant } );
1292
+ return lit;
1293
+ }
1294
+ if (lit === 'number' && type === 'string') // special case, not a quoted literal in CDL
1295
+ return lit;
1298
1296
  error( 'syntax-expected-valid', location(true), { prop: spec.msgProp },
1299
1297
  'Expected valid string for property $(PROP)' );
1300
- return ignore( val );
1298
+ return ignore( lit );
1301
1299
  }
1302
1300
 
1303
1301
  function func( val, spec, xsn ) {
@@ -1535,7 +1533,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
1535
1533
  const zero = s.vZeroFor;
1536
1534
  if (zero) { // (potential) CSN v0.1.0 property
1537
1535
  const group = s.xorGroup;
1538
- if (zero && expected( zero, schema[zero] ) && !(group && xor[group])) {
1536
+ if (expected( zero, schema[zero] ) && !(group && xor[group])) {
1539
1537
  replaceZeroProp( prop, zero );
1540
1538
  if (group)
1541
1539
  xor[group] = prop;
@@ -1555,7 +1553,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
1555
1553
  std: 'CSN property $(PROP) is not expected in $(OTHERPROP)',
1556
1554
  top: 'CSN property $(PROP) is not expected top-level',
1557
1555
  def: 'CSN property $(PROP) is not expected by a definition of kind $(KIND)',
1558
- extend: 'CSN property $(PROP) is not expected by an extend in $(OTHERPROP))',
1556
+ extend: 'CSN property $(PROP) is not expected by an extend in $(OTHERPROP)',
1559
1557
  annotate: 'CSN property $(PROP) is not expected by an annotate in $(OTHERPROP)',
1560
1558
  } );
1561
1559
  // TODO: or still augment it? (but then also handle xorGroup)
@@ -1783,7 +1781,7 @@ function toXsn( csn, filename, options, messageFunctions ) {
1783
1781
  }
1784
1782
 
1785
1783
 
1786
- function augment( csn, filename = 'csn.json', options = {}, messageFunctions ) {
1784
+ function augment( csn, filename = 'csn.json', options = {}, messageFunctions = {} ) {
1787
1785
  try {
1788
1786
  return toXsn( csn, filename, options, messageFunctions );
1789
1787
  }
@@ -1793,7 +1791,7 @@ function augment( csn, filename = 'csn.json', options = {}, messageFunctions ) {
1793
1791
  }
1794
1792
  }
1795
1793
 
1796
- function parse( source, filename = 'csn.json', options = {}, messageFunctions ) {
1794
+ function parse( source, filename = 'csn.json', options = {}, messageFunctions = {} ) {
1797
1795
  try {
1798
1796
  return augment( JSON.parse(source), filename, options, messageFunctions );
1799
1797
  }
@@ -13,6 +13,7 @@
13
13
 
14
14
  const { locationString } = require('../base/messages');
15
15
  const { isBetaEnabled, isDeprecatedEnabled } = require('../base/model');
16
+ const { pathName } = require('../compiler/utils');
16
17
 
17
18
  const compilerVersion = require('../../package.json').version;
18
19
  const creator = `CDS Compiler v${ compilerVersion }`;
@@ -1218,8 +1219,8 @@ function value( node ) {
1218
1219
  if (node.$inferred && gensrcFlavor)
1219
1220
  return undefined;
1220
1221
  if (node.path) {
1221
- const ref = node.path.map( id => id.id ).join('.');
1222
- return extra( { '=': node.variant ? `${ ref }#${ node.variant.id }` : ref }, node );
1222
+ const ref = pathName( node.path );
1223
+ return extra( { '=': node.variant ? `${ ref }#${ pathName(node.variant.path) }` : ref }, node );
1223
1224
  }
1224
1225
  if (node.literal === 'enum')
1225
1226
  return extra( { '#': node.sym.id }, node );
@@ -12,7 +12,8 @@ const { dictAdd, dictAddArray } = require('../base/dictionaries');
12
12
  const locUtils = require('../base/location');
13
13
  const { parseDocComment } = require('./docCommentParser');
14
14
  const { parseMultiLineStringLiteral } = require('./multiLineStringParser');
15
- const { functionsWithoutParens, specialFunctions } = require('../compiler/builtins');
15
+ const { functionsWithoutParens, specialFunctions, quotedLiteralPatterns } = require('../compiler/builtins');
16
+ const { pathName } = require("../compiler/utils");
16
17
 
17
18
  const $location = Symbol.for('cds.$location');
18
19
 
@@ -62,6 +63,8 @@ Object.assign(GenericAntlrParser.prototype, {
62
63
  info: function(...args) { return _message( this, 'info', ...args ); },
63
64
  attachLocation,
64
65
  assignAnnotation,
66
+ checkExtensionDict,
67
+ handleExtension,
65
68
  startLocation,
66
69
  tokenLocation,
67
70
  valueWithTokenLocation,
@@ -113,40 +116,6 @@ Object.assign(GenericAntlrParser.prototype, {
113
116
  parseMultiLineStringLiteral,
114
117
  });
115
118
 
116
- // Patterns for literal token tests and creation. The value is a map from the
117
- // `prefix` argument of function `quotedliteral` to the following properties:
118
- // - `test_msg`: error message which is issued if `test_fn` or `test_re` fail.
119
- // - `test_fn`: function called with argument `value`, fails falsy return value
120
- // - `test_re`: regular expression, fails if it does not match argument `value`
121
- // - `unexpected_msg`: error message which is issued if `unexpected_char` matches
122
- // - `unexpected_char`: regular expression matching an illegal character in `value`,
123
- // the error location is only correct for a literal <prefix>'<value>'
124
- // - `literal`: the value which is used instead of `prefix` in the AST
125
- // TODO: we might do a range check (consider leap seconds, i.e. max value 60),
126
- // but always allow Feb 29 (no leap year computation)
127
- // TODO: make it a configurable error (syntax-invalid-literal)
128
- // TODO: also use for CSN input
129
- const quotedLiteralPatterns = {
130
- x: {
131
- test_variant: 'uneven-hex',
132
- test_fn: (str => Number.isInteger(str.length / 2)),
133
- unexpected_variant: 'invalid-hex',
134
- unexpected_char: /[^0-9a-f]/i,
135
- },
136
- time: {
137
- test_variant: 'time',
138
- test_re: /^[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?$/,
139
- },
140
- date: {
141
- test_variant: 'date',
142
- test_re: /^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/,
143
- },
144
- timestamp: {
145
- test_variant: 'timestamp',
146
- test_re: /^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}(:[0-9]{2}(\.[0-9]{1,7})?)?$/,
147
- },
148
- };
149
-
150
119
  // Use the following function for language constructs which we (currently)
151
120
  // just being able to parse, in able to run tests from HANA CDS. As soon as we
152
121
  // create ASTs for the language construct and put it into a CSN, a
@@ -355,7 +324,8 @@ function assignAnnotation( art, anno, prefix = '', iHaveVariant ) {
355
324
  const pathname = pathName( path );
356
325
  let absolute = '';
357
326
  if (name.variant) {
358
- absolute = `${ prefix }${ pathname }#${ name.variant.id }`;
327
+ const variant = pathName( name.variant.path );
328
+ absolute = `${ prefix }${ pathname }#${ variant }`;
359
329
  if (iHaveVariant) { // TODO: do we really care in the parser / core compiler?
360
330
  this.error( 'anno-duplicate-variant', [ name.variant.location ],
361
331
  {}, // TODO: params
@@ -382,7 +352,8 @@ function assignAnnotation( art, anno, prefix = '', iHaveVariant ) {
382
352
  dictAddArray( art, prop, anno, (n, location, a) => {
383
353
  this.error( 'syntax-duplicate-anno', [ location ], { anno: n },
384
354
  'Duplicate assignment with $(ANNO)' );
385
- a.$errorReported = true; // do not report again later as anno-duplicate-xyz
355
+ a.$errorReported = 'syntax-duplicate-anno';
356
+ // do not report again later as anno-duplicate-xyz
386
357
  } );
387
358
  }
388
359
  if (!prefix) { // set deprecated $annnotations for cds-lsp
@@ -393,6 +364,36 @@ function assignAnnotation( art, anno, prefix = '', iHaveVariant ) {
393
364
  }
394
365
  }
395
366
 
367
+ function checkExtensionDict( dict ) {
368
+ for (const name in dict) {
369
+ const def = dict[name];
370
+ if (!def.$duplicates)
371
+ continue;
372
+
373
+ const numDefines = (def.kind === 'annotate')
374
+ ? 0
375
+ : def.$duplicates.reduce( addOneForDefinition, addOneForDefinition( 0, def ) );
376
+ this.handleExtension( def, name, numDefines );
377
+ for (const dup of def.$duplicates)
378
+ this.handleExtension( dup, name, numDefines );
379
+ }
380
+ }
381
+
382
+ function addOneForDefinition( count, ext ) {
383
+ return (ext.kind === 'extend') ? count : count + 1;
384
+ }
385
+
386
+ function handleExtension( ext, name, numDefines ) {
387
+ if (ext.kind === 'annotate')
388
+ this.warning( 'syntax-duplicate-annotate', [ ext.name.location ], { name } );
389
+ else if (ext.kind === 'extend')
390
+ this.error( 'syntax-duplicate-extend', [ ext.name.location ],
391
+ { name, '#': (numDefines ? 'define' : 'extend') } );
392
+ else if (numDefines === 1)
393
+ ext.$errorReported = 'syntax-duplicate-extend'; // a definition, but not duplicate
394
+ }
395
+
396
+
396
397
  /**
397
398
  * Return start location of `token`, or the first token matched by the current
398
399
  * rule if `token` is undefined
@@ -504,7 +505,7 @@ function surroundByParens( expr, open, close, asQuery = false ) {
504
505
  }
505
506
 
506
507
  function unaryOpForParens( query, val ) {
507
- const parens = query.$parens;
508
+ const parens = query?.$parens;
508
509
  if (!parens)
509
510
  return query;
510
511
  const location = parens[parens.length - 1];
@@ -743,10 +744,6 @@ function quotedLiteral( token, literal ) {
743
744
  }
744
745
  }
745
746
 
746
- function pathName( path, brokenName ) {
747
- return (path && !path.broken) ? path.map( id => id.id ).join('.') : brokenName;
748
- }
749
-
750
747
  function pushIdent( path, ident, prefix ) {
751
748
  if (!ident) {
752
749
  path.broken = true;
@@ -981,6 +978,9 @@ function handleComposition( cardinality, isComposition ) {
981
978
  }
982
979
 
983
980
  function associationInSelectItem( art ) {
981
+ if (!art.value) // e.g. `expand` without value (for new structures)
982
+ return;
983
+
984
984
  const isPath = art.value.path && art.value.path.length
985
985
  const isIdentifier = isPath && art.value.path.length === 1;
986
986
  if (isIdentifier) {
@@ -398,6 +398,7 @@ artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent`
398
398
  this.error( 'syntax-extend-context', $annotate,
399
399
  { code: 'ANNOTATE artifact', kind: defOnly },
400
400
  'No $(CODE) within $(KIND) extensions' );
401
+ if (!$outer.extensions) $outer.extensions = [];
401
402
  this.meltKeywordToIdentifier();
402
403
  }
403
404
  annotateArtifact[ $art, $outer ] // not kind-specific
@@ -793,6 +794,7 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
793
794
  '{' { $art.elements = this.createDict(); }
794
795
  elementDefOrExtend[ $art ]*
795
796
  '}' { this.finalizeDictOrArray( $art.elements ); }
797
+ { this.checkExtensionDict( $art.elements ); }
796
798
  optionalSemi
797
799
  |
798
800
  requiredSemi
@@ -809,6 +811,7 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
809
811
  '{' { $art.elements = this.createDict(); }
810
812
  elementDefOrExtend[ $art ]*
811
813
  '}' { this.finalizeDictOrArray( $art.elements ); }
814
+ { this.checkExtensionDict( $art.elements ); }
812
815
  optionalSemi
813
816
  |
814
817
  requiredSemi
@@ -834,17 +837,18 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
834
837
  |
835
838
  { this.disallowElementExtension( $elemName, $outer, 'actions' ); }
836
839
  ACTIONS '{' { $art.actions = this.createDict(); }
837
- actionFunctionDef[ $art ]*
840
+ actionFunctionDef[ $art ]* // TODO: no EXTEND in actions? (ok, would just allow annos)
838
841
  '}' { this.finalizeDictOrArray( $art.actions ); }
839
842
  optionalSemi
840
843
  |
841
844
  ELEMENTS '{' { $art.elements = this.createDict(); }
842
845
  elementDefOrExtend[ $art ]*
843
846
  '}' { this.finalizeDictOrArray( $art.elements ); }
847
+ { this.checkExtensionDict( $art.elements ); }
844
848
  optionalSemi
845
849
  |
846
850
  ENUM '{' { $art.enum = this.createDict(); }
847
- enumSymbolDef[ $art ]*
851
+ enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
848
852
  '}' { this.finalizeDictOrArray( $art.enum ); }
849
853
  optionalSemi
850
854
  )
@@ -862,6 +866,7 @@ extendWithOptElements[ art ]
862
866
  '{' { $art.elements = this.createDict(); }
863
867
  elementDefOrExtend[ $art ]*
864
868
  '}' { this.finalizeDictOrArray( $art.elements ); }
869
+ { this.checkExtensionDict( $art.elements ); }
865
870
  optionalSemi
866
871
  |
867
872
  requiredSemi
@@ -873,6 +878,7 @@ extendWithOptElements[ art ]
873
878
  '{' { $art.elements = this.createDict(); }
874
879
  elementDefOrExtend[ $art ]*
875
880
  '}' { this.finalizeDictOrArray( $art.elements ); }
881
+ { this.checkExtensionDict( $art.elements ); }
876
882
  optionalSemi
877
883
  |
878
884
  requiredSemi
@@ -892,11 +898,13 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
892
898
  '{' { $art.elements = this.createDict(); }
893
899
  annotateElement[ $art ]*
894
900
  '}' { this.finalizeDictOrArray( $art.elements ); }
901
+ { this.checkExtensionDict( $art.elements ); }
895
902
  (
896
903
  ACTIONS
897
904
  '{' { $art.actions = this.createDict(); }
898
905
  annotateAction[ $art ]*
899
906
  '}' { this.finalizeDictOrArray( $art.actions ); }
907
+ { this.checkExtensionDict( $art.actions ); }
900
908
  )?
901
909
  optionalSemi
902
910
  |
@@ -904,6 +912,7 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
904
912
  '{' { $art.actions = this.createDict(); }
905
913
  annotateAction[ $art ]*
906
914
  '}' { this.finalizeDictOrArray( $art.actions ); }
915
+ { this.checkExtensionDict( $art.actions ); }
907
916
  optionalSemi
908
917
  |
909
918
  '(' { $art.params = this.createDict(); }
@@ -912,11 +921,13 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
912
921
  annotateParam[ $art ]
913
922
  )*
914
923
  ')' { this.finalizeDictOrArray( $art.params ); }
924
+ { this.checkExtensionDict( $art.params ); }
915
925
  (
916
926
  RETURNS { $art['$'+'syntax'] = 'returns'; }
917
927
  '{' { $art.elements = this.createDict(); }
918
928
  annotateElement[ $art ]*
919
929
  '}' { this.finalizeDictOrArray( $art.elements ); }
930
+ { this.checkExtensionDict( $art.elements ); }
920
931
  optionalSemi
921
932
  |
922
933
  requiredSemi
@@ -926,6 +937,7 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
926
937
  '{' { $art.elements = this.createDict(); }
927
938
  annotateElement[ $art ]*
928
939
  '}' { this.finalizeDictOrArray( $art.elements ); }
940
+ { this.checkExtensionDict( $art.elements ); }
929
941
  optionalSemi
930
942
 
931
943
  |
@@ -946,6 +958,7 @@ annotateElement[ outer ] locals[ art = {} ]
946
958
  '{' { $art.elements = this.createDict(); }
947
959
  annotateElement[ $art ]*
948
960
  '}' { this.finalizeDictOrArray( $art.elements ); }
961
+ { this.checkExtensionDict( $art.elements ); }
949
962
  optionalSemi
950
963
  |
951
964
  requiredSemi
@@ -968,11 +981,13 @@ annotateAction [ outer ] locals [ art = {} ]
968
981
  annotateParam[ $art ]
969
982
  )*
970
983
  ')' { this.finalizeDictOrArray( $art.params ); }
984
+ { this.checkExtensionDict( $art.params ); }
971
985
  )?
972
986
  (
973
987
  RETURNS '{' { $art.elements = this.createDict(); }
974
988
  annotateElement[ $art ]*
975
989
  '}' { this.finalizeDictOrArray( $art.elements ); }
990
+ { this.checkExtensionDict( $art.elements ); }
976
991
  optionalSemi
977
992
  |
978
993
  requiredSemi
@@ -1831,7 +1846,7 @@ typeNamedArg[ art ] locals[ arg = '' ]
1831
1846
  :
1832
1847
  name=ident['paramname']
1833
1848
  ':'
1834
- { if (this.checkTypeFacet( $art, $name.id ))
1849
+ { if ($name.id && this.checkTypeFacet( $art, $name.id ))
1835
1850
  $arg = $name.id.id;
1836
1851
  }
1837
1852
  (
@@ -1868,10 +1883,10 @@ queryExpression returns[ query ] // QLSubqueryComplex, SubqueryComplex
1868
1883
  | op=MINUS q=DISTINCT?
1869
1884
  )
1870
1885
  qt=queryTerm
1871
- { $query = this.leftAssocBinaryOp( $query, $op, $q, $qt.query );; $ctx.q = null; }
1886
+ { if ($qt.query) $query = this.leftAssocBinaryOp( $query, $op, $q, $qt.query );; $ctx.q = null; }
1872
1887
  )*
1873
- ( ob=orderByClause[ $query ] { $query = $ob.query; } ) ?
1874
- ( lc=limitClause[ $query ] { $query = $lc.query; } ) ?
1888
+ ( ob=orderByClause[ $query ] { if ($ob.query) $query = $ob.query; } ) ?
1889
+ ( lc=limitClause[ $query ] { if ($lc.query) $query = $lc.query; } ) ?
1875
1890
  ;
1876
1891
 
1877
1892
  orderByClause[ inQuery ] returns [ query ]
@@ -2075,7 +2090,7 @@ tableExpression returns[ table ] // TableOrJoin
2075
2090
  ON cond=condition { $table.on = $cond.cond; }
2076
2091
  |
2077
2092
  crj=CROSS jn=JOIN tt=tableTerm
2078
- { $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
2093
+ { if (!$table) { $table = {}; } $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
2079
2094
  )*
2080
2095
  ;
2081
2096
 
@@ -2257,9 +2272,11 @@ conditionTerm returns [ cond ]
2257
2272
  |
2258
2273
  { $cond = { args: [ $expr.expr ] }; }
2259
2274
  NOT predicate[ $cond, true ]
2275
+ { if (!$cond.op) $cond = null; } // predicate failed to parse, avoid subseqential errors
2260
2276
  |
2261
2277
  { $cond = { args: [ $expr.expr ] }; }
2262
2278
  predicate[ $cond, false ]
2279
+ { if (!$cond.op) $cond = null; } // predicate failed to parse, avoid subseqential errors
2263
2280
  )? // optional: for conditions in parentheses
2264
2281
  ;
2265
2282
 
@@ -2687,10 +2704,7 @@ annoValueBase[ assignment ] locals [ seenEllipsis = false ]
2687
2704
  flattenedValue[ assignment ] locals[ val = { name: {} } ]
2688
2705
  :
2689
2706
  at='@'? annotationPath[ $val.name, 'name', $at ]
2690
- (
2691
- '#' { this.meltKeywordToIdentifier(); }
2692
- variant=ident['variant'] { $val.name.variant = $variant.id; }
2693
- )?
2707
+ ( annotationPathVariant[ $val.name ] )?
2694
2708
  (
2695
2709
  ':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
2696
2710
  annoValue[ $val ]
@@ -2739,10 +2753,7 @@ annoSubValue returns[ val = {} ]
2739
2753
  { Object.assign( $val, this.numberLiteral( $num, $plus||$min ) ); }
2740
2754
  |
2741
2755
  at='@'? annotationPath[ $val, 'ref', $at ]
2742
- (
2743
- '#' { this.meltKeywordToIdentifier(); }
2744
- variant=ident['variant'] { $val.variant = $variant.id; }
2745
- )?
2756
+ ( annotationPathVariant[ $val ] )?
2746
2757
  ;
2747
2758
 
2748
2759
  literalValue returns[ val ] locals[ tok ]
@@ -2800,12 +2811,12 @@ annotationPath[ art, category, headat = null ] locals[ _sync = 'nop' ]
2800
2811
  )*
2801
2812
  ;
2802
2813
 
2803
- annotationPathVariant[ art ]
2814
+ annotationPathVariant[ art ] locals[ variant = {} ]
2804
2815
  @after { this.attachLocation($art); }
2805
2816
  :
2806
2817
  // TODO: warning for space after '#'
2807
2818
  '#' { this.meltKeywordToIdentifier(); }
2808
- variant=ident['variant'] { $art.variant = $variant.id; }
2819
+ simplePath[ $variant, 'variant' ] { $art.variant = $variant; }
2809
2820
  ;
2810
2821
 
2811
2822
  // Identifier and non-reserved keywords --------------------------------------
@@ -907,6 +907,7 @@ function startCsnPath( csnPath, csn ) {
907
907
  /**
908
908
  * @param {CSN.Path} csnPath
909
909
  * @param {CSN.Model} csn
910
+ * @param {any} resolve
910
911
  */
911
912
  function analyseCsnPath( csnPath, csn, resolve ) {
912
913
  /** @type {object} */
@@ -1056,7 +1056,7 @@ function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
1056
1056
  */
1057
1057
  // eslint-disable-next-line no-unused-vars
1058
1058
  function getElementDatabaseNameOf(elemName, sqlMapping, sqlDialect='plain') {
1059
- isValidMappingDialectCombi(sqlMapping, sqlDialect)
1059
+ isValidMappingDialectCombi(sqlDialect, sqlMapping)
1060
1060
  if (sqlMapping === 'hdbcds') {
1061
1061
  return elemName;
1062
1062
  }
@@ -1280,19 +1280,30 @@ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
1280
1280
  * @todo Does _not_ apply param/action/... annotations.
1281
1281
  *
1282
1282
  * @param {CSN.Model} csn
1283
- * @param {{overwrite?: boolean, filter?: (name: string) => boolean}} config
1283
+ * @param {{notFound?: (name: string, index: number) => void, override?: boolean, filter?: (name: string) => boolean}} config
1284
+ * notFound: Function that is called if the referenced definition can't be found.
1285
+ * Second argument is index in `csn.extensions` array.
1286
+ * override: Whether to ignore existing annotations.
1287
+ * filter: Positive filter. If it returns true, annotations for the referenced artifact
1288
+ * will be applied.
1289
+ * @todo Improve to also apply element annotations
1284
1290
  */
1285
1291
  function applyAnnotationsFromExtensions(csn, config) {
1286
1292
  if (!csn.extensions)
1287
1293
  return;
1288
1294
 
1289
1295
  const filter = config.filter || ((_name) => true);
1290
- for (const ext of csn.extensions) {
1296
+ for (let i = 0; i < csn.extensions.length; ++i) {
1297
+ const ext = csn.extensions[i];
1291
1298
  const name = ext.annotate || ext.extend;
1292
- const def = csn.definitions[name];
1293
- if (name && def && filter(name)) {
1294
- copyAnnotationsAndDoc(ext, def, config.overwrite);
1295
- applyAnnotationsToElements(ext, def);
1299
+ if (name && filter(name)) {
1300
+ const def = csn.definitions[name];
1301
+ if (def) {
1302
+ copyAnnotationsAndDoc(ext, def, config.override);
1303
+ applyAnnotationsToElements(ext, def);
1304
+ } else if (config.notFound) {
1305
+ config.notFound(name, i);
1306
+ }
1296
1307
  }
1297
1308
  }
1298
1309
 
@@ -1309,7 +1320,7 @@ function applyAnnotationsFromExtensions(csn, config) {
1309
1320
  forEach(ext.elements, (key, sourceElem) => {
1310
1321
  const targetElem = def.elements[key];
1311
1322
  if (targetElem) {
1312
- copyAnnotationsAndDoc(sourceElem, targetElem, config.overwrite);
1323
+ copyAnnotationsAndDoc(sourceElem, targetElem, config.override);
1313
1324
  applyAnnotationsToElements(sourceElem, targetElem);
1314
1325
  }
1315
1326
  });
@@ -131,7 +131,10 @@ function revealInternalProperties( model, nameOrPath ) {
131
131
 
132
132
  path = path.split('/');
133
133
  if (path.length === 1) {
134
- return reveal( xsn.definitions[path] || xsn.vocabularies && xsn.vocabularies[path] );
134
+ const def = xsn.definitions?.[path[0]] || xsn.vocabularies?.[path[0]];
135
+ if (!def)
136
+ throw new Error(`reveal xsn: Unknown definition: “${path[0]}”`)
137
+ return reveal( def );
135
138
  }
136
139
 
137
140
  // with the code below, we might miss the right transformer function