@sap/cds-compiler 2.5.0 → 2.10.4

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 (92) hide show
  1. package/CHANGELOG.md +191 -9
  2. package/bin/cdsc.js +2 -2
  3. package/doc/CHANGELOG_BETA.md +33 -3
  4. package/lib/api/main.js +29 -101
  5. package/lib/api/options.js +15 -11
  6. package/lib/api/validate.js +12 -8
  7. package/lib/backends.js +0 -81
  8. package/lib/base/keywords.js +32 -2
  9. package/lib/base/message-registry.js +63 -9
  10. package/lib/base/messages.js +63 -21
  11. package/lib/base/model.js +2 -3
  12. package/lib/checks/defaultValues.js +27 -2
  13. package/lib/checks/elements.js +1 -6
  14. package/lib/checks/foreignKeys.js +0 -6
  15. package/lib/checks/managedWithoutKeys.js +17 -0
  16. package/lib/checks/nonexpandableStructured.js +38 -0
  17. package/lib/checks/onConditions.js +9 -45
  18. package/lib/checks/queryNoDbArtifacts.js +25 -7
  19. package/lib/checks/selectItems.js +25 -2
  20. package/lib/checks/types.js +26 -2
  21. package/lib/checks/unknownMagic.js +38 -0
  22. package/lib/checks/utils.js +61 -0
  23. package/lib/checks/validator.js +60 -7
  24. package/lib/compiler/assert-consistency.js +16 -7
  25. package/lib/compiler/builtins.js +2 -0
  26. package/lib/compiler/checks.js +6 -4
  27. package/lib/compiler/definer.js +99 -42
  28. package/lib/compiler/index.js +73 -27
  29. package/lib/compiler/resolver.js +288 -157
  30. package/lib/compiler/shared.js +31 -11
  31. package/lib/edm/annotations/genericTranslation.js +182 -186
  32. package/lib/edm/csn2edm.js +103 -108
  33. package/lib/edm/edm.js +18 -21
  34. package/lib/edm/edmPreprocessor.js +361 -114
  35. package/lib/edm/edmUtils.js +103 -33
  36. package/lib/gen/Dictionary.json +22 -0
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +12 -1
  39. package/lib/gen/language.tokens +57 -53
  40. package/lib/gen/languageLexer.interp +10 -1
  41. package/lib/gen/languageLexer.js +770 -744
  42. package/lib/gen/languageLexer.tokens +49 -46
  43. package/lib/gen/languageParser.js +4713 -4279
  44. package/lib/json/from-csn.js +103 -45
  45. package/lib/json/to-csn.js +296 -117
  46. package/lib/language/antlrParser.js +4 -3
  47. package/lib/language/errorStrategy.js +1 -0
  48. package/lib/language/genericAntlrParser.js +21 -12
  49. package/lib/language/language.g4 +99 -31
  50. package/lib/main.d.ts +81 -3
  51. package/lib/main.js +30 -7
  52. package/lib/model/api.js +78 -0
  53. package/lib/model/csnRefs.js +329 -142
  54. package/lib/model/csnUtils.js +235 -58
  55. package/lib/model/enrichCsn.js +18 -1
  56. package/lib/model/revealInternalProperties.js +2 -1
  57. package/lib/modelCompare/compare.js +37 -20
  58. package/lib/optionProcessor.js +9 -3
  59. package/lib/render/.eslintrc.json +4 -1
  60. package/lib/render/DuplicateChecker.js +8 -5
  61. package/lib/render/toCdl.js +112 -33
  62. package/lib/render/toHdbcds.js +134 -64
  63. package/lib/render/toSql.js +91 -38
  64. package/lib/render/utils/common.js +8 -13
  65. package/lib/render/utils/sql.js +3 -3
  66. package/lib/sql-identifier.js +6 -1
  67. package/lib/transform/db/assertUnique.js +5 -6
  68. package/lib/transform/db/constraints.js +29 -13
  69. package/lib/transform/db/draft.js +8 -6
  70. package/lib/transform/db/expansion.js +582 -0
  71. package/lib/transform/db/flattening.js +325 -0
  72. package/lib/transform/db/groupByOrderBy.js +2 -2
  73. package/lib/transform/db/transformExists.js +284 -63
  74. package/lib/transform/forHanaNew.js +98 -381
  75. package/lib/transform/forOdataNew.js +21 -22
  76. package/lib/transform/localized.js +37 -10
  77. package/lib/transform/odata/attachPath.js +19 -4
  78. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  79. package/lib/transform/odata/referenceFlattener.js +60 -39
  80. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  81. package/lib/transform/odata/structuralPath.js +72 -0
  82. package/lib/transform/odata/structureFlattener.js +19 -18
  83. package/lib/transform/odata/typesExposure.js +22 -12
  84. package/lib/transform/transformUtilsNew.js +134 -78
  85. package/lib/transform/translateAssocsToJoins.js +17 -14
  86. package/lib/transform/universalCsnEnricher.js +67 -0
  87. package/lib/utils/file.js +0 -11
  88. package/lib/utils/moduleResolve.js +6 -8
  89. package/package.json +1 -1
  90. package/lib/json/walker.js +0 -26
  91. package/lib/transform/sqlite +0 -0
  92. package/lib/utils/string.js +0 -17
@@ -10,7 +10,7 @@
10
10
 
11
11
  const antlr4 = require('antlr4');
12
12
 
13
- const { makeMessageFunction, CompileMessage } = require('../base/messages');
13
+ const { CompileMessage } = require('../base/messages');
14
14
  const errorStrategy = require('./errorStrategy');
15
15
 
16
16
  const Parser = require('../gen/languageParser').languageParser;
@@ -112,7 +112,7 @@ const rules = {
112
112
  expr: { func: 'conditionEOF', returns: 'cond' }, // yes, condition
113
113
  };
114
114
 
115
- function parse( source, filename = '<undefined>.cds', options = {}, rule = 'cdl' ) {
115
+ function parse( source, filename = '<undefined>.cds', options = {}, messageFunctions, rule = 'cdl' ) {
116
116
  const lexer = new Lexer( new antlr4.InputStream(source) );
117
117
  const tokenStream = new RewriteTypeTokenStream(lexer);
118
118
  /** @type {object} */
@@ -121,7 +121,7 @@ function parse( source, filename = '<undefined>.cds', options = {}, rule = 'cdl'
121
121
 
122
122
  parser.filename = filename;
123
123
  parser.options = options;
124
- parser.$message = makeMessageFunction( parser, options, 'parse' ); // sets parser.messages
124
+ parser.$messageFunctions = messageFunctions;
125
125
 
126
126
  initTokenRewrite( parser, tokenStream );
127
127
  // comment the following 2 lines if you want to output the parser errors directly:
@@ -162,6 +162,7 @@ function parse( source, filename = '<undefined>.cds', options = {}, rule = 'cdl'
162
162
  if (rulespec.$frontend)
163
163
  ast.$frontend = rulespec.$frontend;
164
164
 
165
+ // TODO: clarify with LSP colleagues: still necessary?
165
166
  if (parser.messages) {
166
167
  Object.defineProperty( ast, 'messages',
167
168
  { value: parser.messages, configurable: true, writable: true } );
@@ -245,6 +245,7 @@ function reportIgnoredWith( recognizer, t ) {
245
245
  }
246
246
 
247
247
  function consumeUntil( recognizer, set ) {
248
+ // TODO: add trace
248
249
  if (SEMI == null)
249
250
  SEMI = recognizer.literalNames.indexOf( "';'" );
250
251
  if (RBRACE == null)
@@ -13,6 +13,14 @@ const locUtils = require('../base/location');
13
13
  const { parseDocComment } = require('./docCommentParser');
14
14
  const { functionsWithoutParens, specialFunctions } = require('../compiler/builtins');
15
15
 
16
+
17
+ // Push message `msg` with location `loc` to array of errors:
18
+ function _message( parser, severity, id, loc, ...args ) {
19
+ const msg = parser.$messageFunctions[severity]; // set in antlrParser.js
20
+ return msg( id,
21
+ (loc instanceof antlr4.CommonToken) ? parser.tokenLocation(loc) : loc, ...args );
22
+ }
23
+
16
24
  // Class which is to be used as grammar option with
17
25
  // grammar <name> options { superclass = genericAntlrParser; }
18
26
  //
@@ -34,10 +42,10 @@ function GenericAntlrParser( ...args ) {
34
42
 
35
43
  GenericAntlrParser.prototype = Object.assign(
36
44
  Object.create( antlr4.Parser.prototype ), {
37
- message: function(...args) { return message.call(this, 'message', ...args); },
38
- error: function(...args) { return message.call(this, 'error', ...args); },
39
- warning: function(...args) { return message.call(this, 'warning', ...args); },
40
- info: function(...args) { return message.call(this, 'info', ...args); },
45
+ message: function(...args) { return _message( this, 'message', ...args ); },
46
+ error: function(...args) { return _message( this, 'error', ...args ); },
47
+ warning: function(...args) { return _message( this, 'warning', ...args ); },
48
+ info: function(...args) { return _message( this, 'info', ...args ); },
41
49
  attachLocation,
42
50
  startLocation,
43
51
  tokenLocation,
@@ -69,6 +77,7 @@ GenericAntlrParser.prototype = Object.assign(
69
77
  noAssignmentInSameLine,
70
78
  noSemicolonHere,
71
79
  setLocalToken,
80
+ setLocalTokenIfBefore,
72
81
  excludeExpected,
73
82
  isStraightBefore,
74
83
  meltKeywordToIdentifier,
@@ -110,14 +119,6 @@ const quotedLiteralPatterns = {
110
119
  },
111
120
  };
112
121
 
113
-
114
- // Push message `msg` with location `loc` to array of errors:
115
- function message( severity, id, loc, ...args ) {
116
- const msg = this.$message[severity];
117
- return msg( id, // function $message is set in antlrParser.js
118
- (loc instanceof antlr4.CommonToken) ? this.tokenLocation(loc) : loc, ...args );
119
- }
120
-
121
122
  // Use the following function for language constructs which we (currently)
122
123
  // just being able to parse, in able to run tests from HANA CDS. As soon as we
123
124
  // create ASTs for the language construct and put it into a CSN, a
@@ -183,6 +184,14 @@ function setLocalToken( string, tokenName, notBefore, inSameLine ) {
183
184
  ll1.type = this.constructor[tokenName];
184
185
  }
185
186
 
187
+ function setLocalTokenIfBefore( string, tokenName, before, inSameLine ) {
188
+ const ll1 = this.getCurrentToken();
189
+ if (ll1.text.toUpperCase() === string &&
190
+ (!inSameLine || this._input.LT(-1).line === ll1.line) &&
191
+ (!before || before && before.test( this._input.LT(2).text )))
192
+ ll1.type = this.constructor[tokenName];
193
+ }
194
+
186
195
  // // Special function for rule `requiredSemi` before return $ctx
187
196
  // function braceForSemi() {
188
197
  // if (RBRACE == null)
@@ -20,7 +20,7 @@
20
20
  // allow integers at certain places), one token type for non-quoted and
21
21
  // quoted identifiers.
22
22
  //
23
- // * Keep the number of keywords as small as possibile. Thus, built-ins is a
23
+ // * Keep the number of keywords as small as possible. Thus, built-ins is a
24
24
  // topic for the semantic analysis, not the grammar. Examples: no keywords
25
25
  // for built-in types or built-in SQL functions. This also avoids noise in
26
26
  // the grammar and a huge/slow generated parser.
@@ -43,7 +43,7 @@
43
43
  // is not enough for a decision), write a comment starting with `#ATN:`
44
44
  // which describes the ambiguity. Additionally, put a comment `/* #ATN n
45
45
  // */` INSIDE an (`@after`) action of a rule if the corresponding function
46
- // in '../gen/langageParser.js' contains `n` occurrences of
46
+ // in '../gen/languageParser.js' contains `n` occurrences of
47
47
  // `adaptivePredict` calls. This is checked in 'test/testCompiler.js',
48
48
  // which also counts the total number of `adaptivePredict` occurrences.
49
49
  // └─────────────────────────────────────────────────────────────────────────┘
@@ -55,6 +55,10 @@
55
55
  // good idea anyway to avoid calls to `adaptivePredict`, see the rules
56
56
  // starting with `annotationAssignment_`.
57
57
  //
58
+ // * Factoring out a sub rule into a named rule influences the error recovery:
59
+ // the parser tries to consume all tokens which are neither in the follow
60
+ // set of loops and named rules. So be careful.
61
+ //
58
62
  // * Do not use actions in the lexer. Examples: de-quote string literals not
59
63
  // in the lexer, but in the parser; do not throw errors, but produce error
60
64
  // tokens if necessary.
@@ -106,6 +110,9 @@
106
110
  //
107
111
  // * The ANTLR error "missing attribute access on rule reference c in $c" can
108
112
  // be solved with using $ctx.c instead of $c
113
+ //
114
+ // * If you want to set a property starting with '$' like $syntax, use
115
+ // obj['$'+'syntax'] as the ANTLR tool would replace $syntax by $ctx.syntax
109
116
 
110
117
  grammar language;
111
118
  options {
@@ -114,6 +121,7 @@ options {
114
121
  }
115
122
  tokens {
116
123
  VIRTUAL, // used with setLocalToken()
124
+ OVER, // used with setLocalTokenIfBefore()
117
125
  HelperToken1, // used with setLocalToken(), does not appear in messages
118
126
  HelperToken2, // used with setLocalToken(), does not appear in messages
119
127
  HideAlternatives, // hide alternative tokens (no token seq!)
@@ -818,7 +826,7 @@ annotateArtifact[ outer, loc, annos ] locals[ art, name = {} ]
818
826
  )*
819
827
  ')'
820
828
  (
821
- RETURNS '{'
829
+ RETURNS '{' { $art['$'+'syntax'] = 'returns'; }
822
830
  annotateElement[ $art ]*
823
831
  '}'
824
832
  optionalSemi
@@ -826,7 +834,7 @@ annotateArtifact[ outer, loc, annos ] locals[ art, name = {} ]
826
834
  requiredSemi
827
835
  )
828
836
  |
829
- RETURNS '{'
837
+ RETURNS '{' { $art['$'+'syntax'] = 'returns'; }
830
838
  annotateElement[ $art ]*
831
839
  '}'
832
840
  optionalSemi
@@ -1124,13 +1132,7 @@ bracedSelectItemListDef[ query ]
1124
1132
  '{'
1125
1133
  { if (!$query.columns) $query.columns = []; } // set it early to avoid "wildcard" errors
1126
1134
  (
1127
- ( star='*' // TODO: allow everywhere
1128
- {
1129
- $query.columns = [ this.tokenLocation( $star, undefined, '*' ) ];
1130
- }
1131
- |
1132
- selectItemDef[ $query.columns ]
1133
- )
1135
+ selectItemDef[ $query.columns ]
1134
1136
  ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1135
1137
  selectItemDef[ $query.columns ]
1136
1138
  )*
@@ -1139,8 +1141,11 @@ bracedSelectItemListDef[ query ]
1139
1141
  ;
1140
1142
 
1141
1143
  selectItemDef[ outer ] locals[ annos = [] ]
1142
- @after{ this.attachLocation($art.art); }
1144
+ @after{ if ($ctx.art) this.attachLocation($art.art); }
1143
1145
  :
1146
+ star='*'
1147
+ { $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
1148
+ |
1144
1149
  { this.docComment( $annos ); }
1145
1150
  annotationAssignment_atn[ $annos ]*
1146
1151
  // VIRTUAL is keyword, except if before the following tokens texts:
@@ -1159,6 +1164,8 @@ selectItemDefBody[ outer, annos ] returns[ art = {} ]
1159
1164
  :
1160
1165
  (
1161
1166
  e=expression
1167
+ // we cannot use 'condition' instead, as long as we allow aliases without
1168
+ // AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
1162
1169
  {
1163
1170
  $art = this.addItem( $outer, null, null, $annos, { value: $e.expr } );
1164
1171
  }
@@ -1218,11 +1225,7 @@ selectItemInlineList[ art, clause ]
1218
1225
  '{'
1219
1226
  { $art[$clause] = []; }
1220
1227
  (
1221
- ( star='*' // TODO: allow everywhere
1222
- { $art[$clause].push( this.tokenLocation( $star, undefined, '*' ) ); }
1223
- |
1224
- selectItemInlineDef[ $art[$clause] ]
1225
- )
1228
+ selectItemInlineDef[ $art[$clause] ]
1226
1229
  ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1227
1230
  selectItemInlineDef[ $art[$clause] ]
1228
1231
  )*
@@ -1231,8 +1234,11 @@ selectItemInlineList[ art, clause ]
1231
1234
  ;
1232
1235
 
1233
1236
  selectItemInlineDef[ outer ] locals[ annos = [] ]
1234
- @after{ this.attachLocation($art.art); }
1237
+ @after{ if ($ctx.art) this.attachLocation($art.art); }
1235
1238
  :
1239
+ star='*'
1240
+ { $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
1241
+ |
1236
1242
  { this.docComment( $annos ); }
1237
1243
  annotationAssignment_atn[ $annos ]*
1238
1244
  art=selectItemDefBody[ $outer, $annos ]
@@ -1407,6 +1413,8 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1407
1413
  |
1408
1414
  ':'
1409
1415
  // #ATN: typeRefOptArgs can start with ARRAY or MANY or ASSOCIATION or TYPE or LOCALIZED
1416
+ // Nevertheless, MANY '{' is handled by local token rewrite:
1417
+ { this.setLocalToken( 'MANY', 'HelperToken1', /^[^\{]/ ); }
1410
1418
  (
1411
1419
  typeStruct[ $art ]
1412
1420
  optionalSemi
@@ -1416,6 +1424,12 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1416
1424
  ( typeToMany[ $art ] | typeToOne[ $art ] | simplePath[ $art.target, 'artref' ] )
1417
1425
  typeAssociationCont[ $art ]?
1418
1426
  requiredSemi // and if its the ';'...
1427
+ |
1428
+ many=HelperToken1 // rewritten MANY before '{'
1429
+ { $art.items = { location: this.tokenLocation( $many ) };}
1430
+ typeStruct[ $art.items ]
1431
+ nullability[ $art.items ]?
1432
+ optionalSemi
1419
1433
  |
1420
1434
  (
1421
1435
  array=ARRAY of=OF
@@ -1455,6 +1469,7 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1455
1469
  annotationAssignment_ll1[ $annos ]*
1456
1470
  requiredSemi
1457
1471
  |
1472
+ // alt lookahead includes MANY '{'
1458
1473
  { $art.type = {}; }
1459
1474
  simplePath[ $art.type, 'artref' ]
1460
1475
  (
@@ -1462,8 +1477,20 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1462
1477
  head=Number
1463
1478
  { $art['$'+'typeArgs'] = [ this.numberLiteral( $head ) ]; }
1464
1479
  ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1465
- tail=Number
1466
- { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1480
+ (
1481
+ v=VARIABLE
1482
+ { $art['$'+'typeArgs'].push(
1483
+ { literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
1484
+ }
1485
+ |
1486
+ f=FLOATING
1487
+ { $art['$'+'typeArgs'].push(
1488
+ { literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
1489
+ }
1490
+ |
1491
+ tail=Number
1492
+ { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1493
+ )
1467
1494
  )*
1468
1495
  ')'
1469
1496
  { this.docComment( $annos ); }
@@ -1687,8 +1714,20 @@ typeRefOptArgs[ art ]
1687
1714
  head=Number
1688
1715
  { $art['$'+'typeArgs'] = [ this.numberLiteral( $head ) ]; }
1689
1716
  ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1690
- tail=Number
1691
- { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1717
+ (
1718
+ v=VARIABLE
1719
+ { $art['$'+'typeArgs'].push(
1720
+ { literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
1721
+ }
1722
+ |
1723
+ f=FLOATING
1724
+ { $art['$'+'typeArgs'].push(
1725
+ { literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
1726
+ }
1727
+ |
1728
+ tail=Number
1729
+ { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1730
+ )
1692
1731
  )*
1693
1732
  ')'
1694
1733
  |
@@ -1723,6 +1762,30 @@ orderByClause[ inQuery ] returns [ query ]
1723
1762
  ( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
1724
1763
  ;
1725
1764
 
1765
+ overOrderByClause returns [ expr ]
1766
+ :
1767
+ o=ORDER b=BY { $expr = { op: this.tokenLocation($o, $b, 'orderBy' ) , args: [] }}
1768
+ ob1=orderBySpec { $expr.args.push( $ob1.ob ); }
1769
+ ( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
1770
+ ;
1771
+
1772
+ partitionByClause returns [ expr ]
1773
+ :
1774
+ p=PARTITION b=BY { $expr = { op: this.tokenLocation($p, $b, 'partitionBy' ) , args: [] }}
1775
+ e1=expression { $expr.args.push( $e1.expr ); }
1776
+ ( ',' en=expression { $expr.args.push( $en.expr ); } )*
1777
+ ;
1778
+
1779
+ overClause returns [ over ]
1780
+ @after { this.attachLocation($over); }
1781
+ :
1782
+ o=OVER { $over = { op: this.tokenLocation( $o, null, 'over' ) , args: [] } }
1783
+ '('
1784
+ ( pb=partitionByClause { $over.args.push( $pb.expr ); } )?
1785
+ ( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
1786
+ ')'
1787
+ ;
1788
+
1726
1789
  limitClause[ inQuery ] returns [ query ]
1727
1790
  :
1728
1791
  limkw=LIMIT { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
@@ -1781,13 +1844,7 @@ queryPrimary returns[ query = {} ]
1781
1844
  { $query.quantifier = this.tokenLocation( $ad, undefined, $ad.text.toLowerCase() ); }
1782
1845
  )?
1783
1846
  { $query.columns = []; } // set it early to avoid "wildcard" errors
1784
- ( star='*'
1785
- {
1786
- $query.columns = [ this.tokenLocation( $star, undefined, '*' ) ];
1787
- }
1788
- |
1789
- selectItemDef[ $query.columns ]
1790
- )
1847
+ selectItemDef[ $query.columns ]
1791
1848
  ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1792
1849
  selectItemDef[ $query.columns ]
1793
1850
  )*
@@ -1945,7 +2002,7 @@ fromPath[ qp, idkind ]
1945
2002
  condition returns [ cond ] locals [ args = [], orl = [] ]
1946
2003
  @after{
1947
2004
  $cond = ($args.length == 1)
1948
- ? $args[0]
2005
+ ? this.attachLocation( $args[0] )
1949
2006
  : this.attachLocation({ op: $orl[0], args: $args });
1950
2007
  }
1951
2008
  :
@@ -2106,6 +2163,10 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2106
2163
  this.notSupportedYet( $ne ); }
2107
2164
  |
2108
2165
  vp=valuePath[ 'ref', null ] { $expr = this.valuePathAst( $vp.qp ); }
2166
+ { this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
2167
+ (
2168
+ over=overClause { $expr.suffix = [ $over.over ] }
2169
+ )?
2109
2170
  |
2110
2171
  ':'
2111
2172
  ( vp=valuePath[ 'paramref', this.startLocation() ]
@@ -2306,7 +2367,7 @@ optionalCardinality[ pathStep ]
2306
2367
  // completion just produces `:`after having inserted a Number - TODO.
2307
2368
  { if (this._input.LT(2).text !== ':') return $ctx; }
2308
2369
  ( trgMax=Number ':'
2309
- { if ($pathStep) $pathStep.cardinality = { targetMax: this.numberLiteral( $trgMax ) } }
2370
+ { if ($pathStep) $pathStep.cardinality = { targetMax: this.numberLiteral( $trgMax ), location: this.startLocation() }; }
2310
2371
  )
2311
2372
  ;
2312
2373
 
@@ -2557,6 +2618,7 @@ ident[ category ] returns[ id ]
2557
2618
  | EXCLUDING
2558
2619
  | EXTEND
2559
2620
  | FIRST
2621
+ | FLOATING
2560
2622
  | FULL
2561
2623
  | FUNCTION
2562
2624
  | GROUP
@@ -2587,6 +2649,7 @@ ident[ category ] returns[ id ]
2587
2649
  | ORDER
2588
2650
  | OUTER
2589
2651
  | PARAMETERS
2652
+ | PARTITION
2590
2653
  | PROJECTION
2591
2654
  | REDIRECTED
2592
2655
  | RETURNS
@@ -2599,6 +2662,7 @@ ident[ category ] returns[ id ]
2599
2662
  | TO
2600
2663
  | TYPE
2601
2664
  | USING
2665
+ | VARIABLE
2602
2666
  | VIEW
2603
2667
  | YEAR
2604
2668
  ;
@@ -2715,6 +2779,7 @@ EXCEPT : [eE][xX][cC][eE][pP][tT] ;
2715
2779
  EXCLUDING : [eE][xX][cC][lL][uU][dD][iI][nN][gG] ;
2716
2780
  EXTEND : [eE][xX][tT][eE][nN][dD] ;
2717
2781
  FIRST : [fF][iI][rR][sS][tT] ;
2782
+ FLOATING : [fF][lL][oO][aA][tT][iI][nN][gG] ;
2718
2783
  FULL : [fF][uU][lL][lL] ;
2719
2784
  FUNCTION : [fF][uU][nN][cC][tT][iI][oO][nN] ;
2720
2785
  GROUP : [gG][rR][oO][uU][pP] ;
@@ -2744,7 +2809,9 @@ ONE : [oO][nN][eE] ;
2744
2809
  OR : [oO][rR] ;
2745
2810
  ORDER : [oO][rR][dD][eE][rR] ;
2746
2811
  OUTER : [oO][uU][tT][eE][rR] ;
2812
+ // OVER : [oO][vV][eE][rR] ;
2747
2813
  PARAMETERS : [pP][aA][rR][aA][mM][eE][tT][eE][rR][sS] ;
2814
+ PARTITION: [pP][aA][rR][tT][iI][tT][iI][oO][nN] ;
2748
2815
  PROJECTION : [pP][rR][oO][jJ][eE][cC][tT][iI][oO][nN] ;
2749
2816
  REDIRECTED : [rR][eE][dD][iI][rR][eE][cC][tT][eE][dD] ;
2750
2817
  RETURNS : [rR][eE][tT][uU][rR][nN][sS] ;
@@ -2757,6 +2824,7 @@ TO : [tT][oO] ; // or make reserved? (is in SQL-92)
2757
2824
  TYPE : [tT][yY][pP][eE] ;
2758
2825
  UNION : [uU][nN][iI][oO][nN] ;
2759
2826
  USING : [uU][sS][iI][nN][gG] ;
2827
+ VARIABLE : [vV][aA][rR][iI][aA][bB][lL][eE] ;
2760
2828
  VIEW : [vV][iI][eE][wW] ;
2761
2829
  // VIRTUAL: [vV][iI][rR][tT][uU][aA][lL] ; see tokens {}
2762
2830
  YEAR : [yY][eE][aA][rR] ;
package/lib/main.d.ts CHANGED
@@ -107,12 +107,81 @@ declare namespace compiler {
107
107
  constructor(messages: any, model: any, text: any, ...args);
108
108
  messages: any[];
109
109
  toString(): string;
110
+ /**
111
+ * If `options.attachValidNames` is set, this non-enumerable property holds the CSN model.
112
+ * @internal
113
+ */
114
+ model?: CSN;
115
+ /**
116
+ * Used by `cdsc` to indicate whether the message was already printed to stderr.
117
+ * @private
118
+ */
119
+ hasBeenReported: boolean;
110
120
  }
111
121
 
112
- export function sortMessages(messages: any[]): void;
113
- export function sortMessagesSeverityAware(messages: any[]): void;
122
+ /**
123
+ * Sort the given messages according to their location. Messages are sorted
124
+ * in ascending order according to their:
125
+ *
126
+ * - file name
127
+ * - start line
128
+ * - start column
129
+ * - end line
130
+ * - end column
131
+ * - semantic location (“home”)
132
+ * - message text
133
+ *
134
+ * If both messages do not have a location, they are sorted by their semantic
135
+ * location and then by their message text. If only one message has a file
136
+ * location, that message is sorted prior to those that don't have one.
137
+ *
138
+ * _Note_: Sorting is done in-place.
139
+ *
140
+ * Example of sorted messages:
141
+ * ```txt
142
+ * A.cds:1:11: Info id-3: First message text (in entity:“E”/element:“c”)
143
+ * A.cds:8:11: Error id-5: Another message text (in entity:“C”/element:“g”)
144
+ * B.cds:3:10: Debug id-7: First message text (in entity:“B”/element:“e”)
145
+ * B.cds:3:12: Warning id-4: Message text (in entity:“B”/element:“d”)
146
+ * B.cds:3:12: Error id-4: Message text (in entity:“B”/element:“e”)
147
+ * ```
148
+ *
149
+ * If you also want to sort according to message's severity,
150
+ * see {@link sortMessagesSeverityAware}.
151
+ *
152
+ * @returns The same messages array as the input parameter.
153
+ */
154
+ export function sortMessages(messages: CompileMessage[]): CompileMessage[];
155
+
156
+ /**
157
+ * Sort the given messages in severity aware order. Messages are sorted first
158
+ * by severity where 'Error' comes first, then 'Warning' and so forth.
159
+ * Messages of the same severity are sorted the same as by {@link sortMessages}.
160
+ *
161
+ * _Note_: Sorting is done in-place.
162
+ *
163
+ * @returns The same messages array as the input parameter.
164
+ */
165
+ export function sortMessagesSeverityAware(messages: CompileMessage[]): CompileMessage[];
114
166
 
115
- export function deduplicateMessages(messages: any[]): void;
167
+ /**
168
+ * Removes duplicate messages from the given messages array without destroying
169
+ * references to the array, i.e. removes them in-place.
170
+ *
171
+ * _Note_: Does NOT keep the original order!
172
+ *
173
+ * Two messages are the same if they have the same message hash (see below).
174
+ * If one of the two is more precise, then it replaces the other.
175
+ * A message is more precise if it is contained in the other or if
176
+ * the first does not have an `endLine`/`endCol`.
177
+ *
178
+ * A “message hash” is the string representation of the message. If the
179
+ * message does not have a semantic location (“home”), the message hash
180
+ * is the result of {@link messageString}. If the message has a semantic
181
+ * location, the file location is stripped before being passed to
182
+ * {@link messageString}.
183
+ */
184
+ export function deduplicateMessages(messages: CompileMessage[]): void;
116
185
 
117
186
  /**
118
187
  * Returns a message string with file- and semantic location if present in compact
@@ -205,6 +274,15 @@ declare namespace compiler {
205
274
  export function preparedCsnToEdmx(csn: CSN, service: string, options: any): any;
206
275
 
207
276
  export namespace parse {
277
+ /**
278
+ * Parse the given CDL in parseCdl mode and return its corresponding CSN representation.
279
+ *
280
+ * @param cdl CDL source as string.
281
+ * @param filename Filename to be used in compiler messages.
282
+ * @param options Compiler options. Note that if `options.messages` is not set, messages will be printed to stderr.
283
+ */
284
+ function cdl(cdl: string, filename: string, options?: Options): any;
285
+
208
286
  /**
209
287
  * Parse the given CQL and return its corresponding CSN representation.
210
288
  *
package/lib/main.js CHANGED
@@ -16,10 +16,12 @@
16
16
  const backends = require('./backends');
17
17
  const { odata, cdl, sql, hdi, hdbcds, edm, edmx } = require('./api/main');
18
18
  const { getArtifactDatabaseNameOf, getElementDatabaseNameOf } = require('./model/csnUtils');
19
- const { sortMessages, sortMessagesSeverityAware, deduplicateMessages } = require('./base/messages');
19
+ const { traverseCsn } = require('./model/api');
20
+ const { createMessageFunctions, sortMessages, sortMessagesSeverityAware, deduplicateMessages } = require('./base/messages');
20
21
 
21
22
  const parseLanguage = require('./language/antlrParser');
22
23
  const { parseX, compileX, compileSyncX, compileSourcesX, InvocationError } = require('./compiler');
24
+ const { define } = require('./compiler/definer');
23
25
 
24
26
  // The compiler version (taken from package.json)
25
27
  function version() {
@@ -31,7 +33,6 @@ const {
31
33
  messageString,
32
34
  messageStringMultiline,
33
35
  messageContext,
34
- handleMessages,
35
36
  hasErrors,
36
37
  explainMessage,
37
38
  hasMessageExplanation
@@ -39,15 +40,34 @@ const {
39
40
 
40
41
  const { compactModel, compactQuery, compactExpr } = require('./json/to-csn')
41
42
 
43
+ function parseCdl( cdl, filename, options = {} ) {
44
+ options = Object.assign( {}, options, { parseCdl: true } );
45
+ const sources = Object.create(null);
46
+ const model = { sources, options };
47
+ const messageFunctions = createMessageFunctions( options, 'parse', model );
48
+ model.$messageFunctions = messageFunctions;
49
+
50
+ const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
51
+ messageFunctions );
52
+ sources[filename] = xsn;
53
+ define( model );
54
+ messageFunctions.throwWithError();
55
+ return compactModel( model );
56
+ }
57
+
42
58
  function parseCql( cdl, filename = '<query>.cds', options = {} ) {
43
- let xsn = parseLanguage( cdl, filename, Object.assign( {parseOnly:true}, options ), 'query' );
44
- handleMessages( xsn, options );
59
+ const messageFunctions = createMessageFunctions( options, 'parse' );
60
+ const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
61
+ messageFunctions, 'query' );
62
+ messageFunctions.throwWithError();
45
63
  return compactQuery( xsn );
46
64
  }
47
65
 
48
66
  function parseExpr( cdl, filename = '<expr>.cds', options = {} ) {
49
- let xsn = parseLanguage( cdl, filename, Object.assign( {parseOnly:true}, options ), 'expr' );
50
- handleMessages( xsn, options );
67
+ const messageFunctions = createMessageFunctions( options, 'parse' );
68
+ const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
69
+ messageFunctions, 'expr' );
70
+ messageFunctions.throwWithError();
51
71
  return compactExpr( xsn );
52
72
  }
53
73
 
@@ -79,7 +99,7 @@ module.exports = {
79
99
  preparedCsnToEdm : (csn, service, options) => { return backends.preparedCsnToEdm(csn, service, options).edmj},
80
100
 
81
101
  // additional API:
82
- parse: { cql: parseCql, expr: parseExpr }, // preferred names
102
+ parse: { cdl: parseCdl, cql: parseCql, expr: parseExpr }, // preferred names
83
103
  /**
84
104
  * @deprecated Use parse.cql instead
85
105
  */
@@ -92,6 +112,9 @@ module.exports = {
92
112
  getArtifactCdsPersistenceName: getArtifactDatabaseNameOf,
93
113
  getElementCdsPersistenceName: getElementDatabaseNameOf,
94
114
 
115
+ // Other API functions:
116
+ traverseCsn,
117
+
95
118
  // INTERNAL functions for the cds-lsp package and friends - before you use
96
119
  // it, you MUST talk with us - there can be potential incompatibilities with
97
120
  // new releases (even having the same major version):