@sap/cds-compiler 5.7.0 → 5.7.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.
package/lib/main.d.ts CHANGED
@@ -172,7 +172,6 @@ declare namespace compiler {
172
172
  withLocations?: boolean|string
173
173
  /**
174
174
  * Use the new non-ANTLR based parser for compilation.
175
- * Experimental flag!
176
175
  *
177
176
  * @since v5.2.0
178
177
  */
@@ -48,7 +48,7 @@ const PRECEDENCE_OF_IN_PREDICATE = 10;
48
48
  const PRECEDENCE_OF_EQUAL = 10;
49
49
 
50
50
  class AstBuildingParser extends BaseParser {
51
- leanConditions = { afterBrace: true };
51
+ leanConditions = { afterBrace: true, fail: true };
52
52
 
53
53
  constructor( lexer, keywords, table, options, messageFunctions ) {
54
54
  super( lexer, keywords, table ); // lexer has file
@@ -171,7 +171,7 @@ class AstBuildingParser extends BaseParser {
171
171
  this.dynamic_.generic = spec ? spec[call.argPos] : specialFunctions[''][1];
172
172
  }
173
173
 
174
- lGenericIntroOrExpr( tryGenericIntro = true ) {
174
+ lGenericIntroOrExpr( _mode, tryGenericIntro = true ) {
175
175
  const { keyword, type } = this.la();
176
176
  // TODO: use lower-case in specialFunctions
177
177
  const text = typeof keyword === 'string' ? keyword.toUpperCase() : type;
@@ -180,37 +180,44 @@ class AstBuildingParser extends BaseParser {
180
180
  if (this.dynamic_.generic?.IN === 'separator')
181
181
  this.prec_ = PRECEDENCE_OF_IN_PREDICATE; // only expressions if `in` is separator
182
182
  if (generic !== 'expr')
183
- return (generic === 'intro') ? 'GenericIntro' : 'Id';
183
+ return (generic === 'intro') ? 'GenericIntro' : type;
184
+ // if both intro and expr: specialFunctions[fn][argPos][token] = 'expr'
184
185
  const next = this.tokens[this.tokenIdx + 1];
185
- if (next.type !== ',' && next.type !== ')' &&
186
+ if (next && next.type !== ',' && next.type !== ')' &&
186
187
  this.dynamic_.generic[next.keyword?.toUpperCase?.()] !== 'separator')
187
188
  return 'GenericIntro';
188
189
  }
189
- return (generic === 'expr') ? 'GenericExpr' : 'Id';
190
+ return (generic === 'expr') ? 'GenericExpr' : type;
190
191
  }
191
192
 
192
193
  lGenericExpr() {
193
- return this.lGenericIntroOrExpr( false );
194
+ return this.lGenericIntroOrExpr( null, false );
194
195
  }
195
196
 
196
- lGenericSeparator() { // TODO: { keyword, type } as arg ?
197
+ lGenericSeparator() {
197
198
  const { keyword, type } = this.la();
198
199
  // TODO: use lower-case in specialFunctions
199
200
  const text = typeof keyword === 'string' ? keyword.toUpperCase() : type;
200
201
  const generic = this.dynamic_.generic?.[text];
201
- return (generic === 'separator') ? 'GenericSeparator' : ',';
202
+ return (generic === 'separator') ? 'GenericSeparator' : type;
202
203
  }
203
204
 
204
205
  addTokenToSet_( set, tokenName, val, collectKeywordsOnly ) {
205
- const realTokens = parserTokens[tokenName] && this.dynamic_.generic?.[parserTokens[tokenName]];
206
- // TODO: avoid 2nd parserTokens dict use, use lower-case in specialFunctions
207
- if (!realTokens) {
208
- super.addTokenToSet_( set, tokenName, val, collectKeywordsOnly );
209
- }
210
- else {
206
+ const token = parserTokens[tokenName];
207
+ // TODO: use lower-case in specialFunctions
208
+ const realTokens = token && this.dynamic_.generic?.[token];
209
+ if (realTokens) {
211
210
  for (const t of realTokens)
212
211
  super.addTokenToSet_( set, t.toLowerCase(), val, collectKeywordsOnly );
213
212
  }
213
+ else if (tokenName === 'DeleteStarFromSet') { // in rule `argumentsAndFilter`
214
+ // TODO: workaround for (`GenericExpr : Id_all | '*'`), see #13485.
215
+ // Works, since `DeleteStarFromSet` comes after `*` (length-sorted):
216
+ delete set['*'];
217
+ }
218
+ else {
219
+ super.addTokenToSet_( set, tokenName, val, collectKeywordsOnly );
220
+ }
214
221
  }
215
222
 
216
223
  inSelectItem( _test, arg ) { // only as action
@@ -503,6 +510,15 @@ class AstBuildingParser extends BaseParser {
503
510
  return this.tokens[this.tokenIdx + 1]?.text !== ':';
504
511
  }
505
512
 
513
+ fail( mode ) {
514
+ // TODO TOOL: the following test belongs to the BaseParser.js:
515
+ if (this.conditionTokenIdx === this.tokenIdx && // tested on same
516
+ this.conditionStackLength == null && // after error recover
517
+ mode !== 'M')
518
+ return false;
519
+ return true; // mode !== 'Y';
520
+ }
521
+
506
522
  // Space handling etc, locations ----------------------------------------------
507
523
 
508
524
  // Use the following method for language constructs which we (currently) do
@@ -722,14 +738,15 @@ class AstBuildingParser extends BaseParser {
722
738
  }
723
739
 
724
740
  taggedIfQuery( query ) {
725
- return (query.op && queryOps[query.op.val])
741
+ // attached actions are run even if rules ends prematurely → query can be
742
+ // undefined
743
+ return (query?.op && queryOps[query.op.val])
726
744
  ? { query, location: query.$parens?.at( -1 ) ?? query.location }
727
745
  : query;
728
746
  }
729
747
 
730
- addNamedArg( args, idToken, expr ) {
731
- expr.name = this.identAst( idToken );
732
- (args.args ?? args)[expr.name.id] = expr;
748
+ addNamedArg( pathItem, idToken, expr ) {
749
+ this.addDef( expr, pathItem, 'args', 0, this.identAst( idToken ) );
733
750
  }
734
751
 
735
752
  ixprAst( args ) {
@@ -128,7 +128,7 @@ usingDeclaration[ source ] locals[ decl = { kind: 'using' } ] // TODO: XsnArtifa
128
128
  '{' { $decl.usings = this.createArray(); }
129
129
  ( usingProxy[ $decl, { kind: 'using' } ]
130
130
  ( ',' | <exitLoop> )
131
- )+
131
+ )*
132
132
  '}'<prepare=afterBrace>
133
133
  { this.finalizeDictOrArray( $decl.usings ); }
134
134
  ( FROM String
@@ -1100,7 +1100,7 @@ queryExpression returns[ default expr = {} ] locals[ op, quantifier ]
1100
1100
  )
1101
1101
  query=queryExpression
1102
1102
  // with same op/quantifier: make left-assoc binary to nary:
1103
- { if ($expr.$parens || $op.val !== $expr.op.val || $quantifier?.val !== $expr.quantifier?.val) $expr = { op, args: [$expr], quantifier, location: { ...$.expr.location } }; } // TODO: ...$
1103
+ { if ($expr.$parens || $op.val !== $expr.op?.val || $quantifier?.val !== $expr.quantifier?.val) $expr = { op, args: [$expr], quantifier, location: { ...$.expr.location } }; } // TODO: ...$
1104
1104
  { $quantifier = undefined; }
1105
1105
  { $expr.args.push( $query ); this.attachLocation( $expr ); }
1106
1106
  )*
@@ -1169,9 +1169,9 @@ tableExpression returns[ default expr = {} ] // TableOrJoin
1169
1169
  (
1170
1170
  join=CROSS JOIN
1171
1171
  { if ($expr?.join?.val !== 'cross' || $expr.$parens) $expr = { op: this.valueWithLocation(), join: this.valueWithLocation( undefined, $join ), args: [ $expr ] }; }
1172
- ( tab=tableOrQueryParens { $expr.args.push( this.taggedIfQuery( $tab ) ); }
1172
+ ( tab=tableOrQueryParens { const r = this.taggedIfQuery( $tab ); if (r) $expr.args.push( r ); }
1173
1173
  { this.attachLocation( $expr ); }
1174
- | tab=fromRefWithOptAlias { $expr.args.push( $tab ); }
1174
+ | tab=fromRefWithOptAlias { if ($tab) $expr.args.push( $tab ); }
1175
1175
  { this.attachLocation( $expr ); }
1176
1176
  )
1177
1177
  |
@@ -1393,11 +1393,11 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact() ]
1393
1393
  OVER { this.pushXprToken( $expr.suffix = [] ); }
1394
1394
  overClause[ $expr.suffix ]
1395
1395
  ( e=expression[ ...{ expr: $expr } ]<atAltStart>
1396
- { Object.assign( $e.location, $expr.location ); $art.value = this.attachLocation( $e )}
1396
+ { Object.assign( $e.location || {}, $expr.location ); $art.value = this.attachLocation( $e )}
1397
1397
  )?
1398
1398
  |
1399
1399
  e=expression[ ...{ expr: $expr } ]<atAltStart>
1400
- { Object.assign( $e.location, $expr.location ); $art.value = this.attachLocation( $e )}
1400
+ { Object.assign( $e.location || {}, $expr.location ); $art.value = this.attachLocation( $e )}
1401
1401
  )
1402
1402
  ( AS Id['ItemAlias'] { $art.name = this.identAst(); }
1403
1403
  | Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
@@ -1533,9 +1533,8 @@ valuePath returns[ default expr = { path: [] } ] locals[ pathItem ]
1533
1533
  )*
1534
1534
  ;
1535
1535
 
1536
- // TODO: ? params
1537
1536
  expression returns[ default expr = {} ]
1538
- //@finally{ if (!$expr?.$parens) this.attachLocation( $expr ); }
1537
+ @finally{ if (this.s == null) this.attachLocation( $expr ); }
1539
1538
  :
1540
1539
  (
1541
1540
  expressionOrQueryParens[ ...$ ]
@@ -1716,7 +1715,8 @@ options{ minTokensMatched = 1 }
1716
1715
  (
1717
1716
  ','<prepare=nextFunctionArgument>
1718
1717
  ( expr=funcExpression { $pathStep.args.push( $expr ); }
1719
- | <exitLoop> // <cond>: only before `)`
1718
+ | DeleteStarFromSet // Workaround for missing feature, see #13485, TODO
1719
+ | <exitLoop> // TODO <cond>: only before `)`
1720
1720
  )
1721
1721
  )*
1722
1722
  ( // ORDER BY in generic functions, e.g. `first_value(id order by name)`
@@ -1725,6 +1725,8 @@ options{ minTokensMatched = 1 }
1725
1725
  orderByClauseAsXpr[ $expr.args ]
1726
1726
  { this.attachLocation( $expr ); }
1727
1727
  )?
1728
+ |
1729
+ DeleteStarFromSet // Workaround for missing feature, see #13485, TODO
1728
1730
  )?
1729
1731
  |
1730
1732
  // TODO: if we want perfect CC and error recovery, `isNamedArg` would work
@@ -1772,6 +1774,7 @@ funcExpression returns[ default expr ] locals[ args ]
1772
1774
  { $expr = this.applyOpToken(); $args = $expr.args; }
1773
1775
  e=expression { $expr.args.push( $e ); }
1774
1776
  )
1777
+ // TODO: some <restrict=Id> here?
1775
1778
  ( options{ lookahead = lGenericSeparator; }
1776
1779
  :
1777
1780
  GenericSeparator
@@ -1929,6 +1932,7 @@ annoAssignParen[ art ]
1929
1932
  :
1930
1933
  '('<prepare=annoInSameLine>
1931
1934
  ( annoAssignBase[ $art ]
1935
+ //( annoAssignErrorRecoveryHelper )?
1932
1936
  ( ',' | <exitLoop> )
1933
1937
  )*
1934
1938
  ')'
@@ -2018,7 +2022,8 @@ annoValue returns[ default value = {} ]
2018
2022
  if ($value.$flatten) $value.$flatten.push( $sub );
2019
2023
  else this.addDef( $sub, $value, 'struct', null, $sub.name );
2020
2024
  }
2021
- ( ',' | <exitLoop> )
2025
+ ( ',' | <exitLoop> | <guard=fail, repeatLoop, restrict=Id> )
2026
+ // <guard=fail, repeatLoop>` for better error recovery for input `foo@bar`
2022
2027
  )*
2023
2028
  // TODO TOOL: allow:
2024
2029
  // ( <guard=arrayAnno, …> '}' | <error> ) // TODO TOOL - workaround:
@@ -21,7 +21,7 @@ function parseCdl( source, filename = '<undefined>.cds',
21
21
  options = {}, messageFunctions = null,
22
22
  rule = 'cdl' ) {
23
23
  const rulespec = rules[rule];
24
- if (!options.newParser) // TODO: (options.newParser === false)
24
+ if (!options.newParser && !options.newparser)
25
25
  return parseWithAntlr( source, filename, options, messageFunctions, rulespec );
26
26
  const { CdlParser } = gen;
27
27
  if (CdlParser.tracingParser) // tracing → direct console output of message
@@ -74,9 +74,7 @@ class DeltaRenderer {
74
74
  alterColumns(artifactName, columnName, delta, definitionsStr, _eltName, _env) {
75
75
  if (Array.isArray(definitionsStr)) {
76
76
  const prefix = `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ALTER (`;
77
- let padding = '';
78
- for (let i = 0; i < prefix.length; i++)
79
- padding += ' ';
77
+ const padding = ' '.repeat(prefix.length);
80
78
  const body = definitionsStr.map(s => padding + s).join(',\n').slice(padding.length); // no padding for first part
81
79
  const postfix = ');';
82
80
  return [ prefix + body + postfix ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "5.7.0",
3
+ "version": "5.7.4",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",