@sap/cds-compiler 6.8.0 → 6.9.1

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 (44) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +4 -0
  3. package/bin/cdshi.js +1 -0
  4. package/lib/api/main.js +8 -1
  5. package/lib/api/options.js +3 -1
  6. package/lib/base/builtins.js +13 -9
  7. package/lib/base/csnRefs.js +8 -10
  8. package/lib/base/message-registry.js +61 -3
  9. package/lib/base/messages.js +2 -0
  10. package/lib/base/optionProcessor.js +2 -0
  11. package/lib/base/specialOptions.js +1 -1
  12. package/lib/compiler/assert-consistency.js +11 -9
  13. package/lib/compiler/base.js +5 -1
  14. package/lib/compiler/define.js +1 -1
  15. package/lib/compiler/dictionaries.js +2 -3
  16. package/lib/compiler/extend.js +137 -27
  17. package/lib/compiler/lsp-api.js +3 -3
  18. package/lib/compiler/populate.js +4 -5
  19. package/lib/compiler/resolve.js +50 -35
  20. package/lib/compiler/shared.js +33 -14
  21. package/lib/compiler/tweak-assocs.js +2 -2
  22. package/lib/compiler/utils.js +26 -23
  23. package/lib/compiler/xpr-rewrite.js +2 -2
  24. package/lib/edm/EdmPrimitiveTypeDefinitions.js +4 -1
  25. package/lib/edm/annotations/genericTranslation.js +49 -6
  26. package/lib/edm/csn2edm.js +4 -2
  27. package/lib/edm/edm.js +6 -3
  28. package/lib/gen/BaseParser.js +59 -97
  29. package/lib/gen/CdlGrammar.checksum +1 -1
  30. package/lib/gen/CdlParser.js +2055 -1969
  31. package/lib/gen/Dictionary.json +67 -7
  32. package/lib/json/from-csn.js +7 -12
  33. package/lib/json/to-csn.js +59 -35
  34. package/lib/parsers/AstBuildingParser.js +43 -30
  35. package/lib/render/toCdl.js +46 -27
  36. package/lib/render/toSql.js +9 -0
  37. package/lib/render/utils/common.js +3 -2
  38. package/lib/tool-lib/enrichCsn.js +1 -0
  39. package/lib/transform/effective/flattening.js +6 -5
  40. package/lib/transform/effective/main.js +5 -0
  41. package/lib/transform/forOdata.js +20 -2
  42. package/lib/transform/forRelationalDB.js +8 -4
  43. package/lib/transform/tupleExpansion.js +40 -0
  44. package/package.json +3 -40
@@ -292,6 +292,8 @@ const testFunctionPlaceholder = () => true;
292
292
  * Return path step if the path navigates along an association whose final type
293
293
  * satisfies function `test`; "navigates along" = last path item not considered
294
294
  * without truthy optional argument `alsoTestLast`.
295
+ *
296
+ * This function assumes that the reference has already been resolved.
295
297
  */
296
298
  function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = false ) {
297
299
  for (const item of ref.path || []) {
@@ -528,6 +530,11 @@ function forEachExprArray( query, array, refContext, exprContext, callback ) {
528
530
  // i.e. is necessary to calculate the elements of the query
529
531
  // except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
530
532
  // NOTE: does not run on non-referred sub queries! Consider using ‹main›.$queries instead!
533
+ //
534
+ // Argument `simpleOnly`:
535
+ // - truthy: only simple queries + sub query in from = do not traverse join and union args
536
+ // - false, '', 0: all, but not right of union/intersect/etc
537
+ // - null, undefined: all, including right side of union/intersect/etc
531
538
  function traverseQueryPost( query, simpleOnly, callback ) {
532
539
  if (!query) // parser error
533
540
  return;
@@ -538,7 +545,7 @@ function traverseQueryPost( query, simpleOnly, callback ) {
538
545
  }
539
546
  if (simpleOnly) {
540
547
  const { from } = query;
541
- if (!from || from.join) // parse error or join
548
+ if (!from || from.join) // parse error or join or union etc
542
549
  return; // ok are: path or simple sub query (!)
543
550
  }
544
551
  if (query.from) { // SELECT
@@ -548,7 +555,7 @@ function traverseQueryPost( query, simpleOnly, callback ) {
548
555
  // console.log('FE:')
549
556
  }
550
557
  else if (query.args) { // JOIN, UNION, INTERSECT
551
- if (!query.join && simpleOnly == null) {
558
+ if (!query.join && simpleOnly != null) {
552
559
  // enough for elements: traverse only first args for UNION/INTERSECT
553
560
  // TODO: we might use this also when we do not rewrite associations
554
561
  // in non-referred sub queries
@@ -576,7 +583,7 @@ function traverseQueryExtra( main, callback ) {
576
583
  if (!main.$queries)
577
584
  return;
578
585
  // with a top-level UNION, $queries[0] is just the left
579
- traverseQueryPost( main.query, false, (q) => { // also with right of UNION (to be compatible)
586
+ traverseQueryPost( main.query, null, (q) => { // also into right of UNION (to be compatible)
580
587
  setLink( q, '_status', 'extra' );
581
588
  callback( q );
582
589
  } );
@@ -584,7 +591,7 @@ function traverseQueryExtra( main, callback ) {
584
591
  if (query._status === 'extra' || query._parent.kind === '$tableAlias')
585
592
  continue; // if parent is alias, query is FROM source -> run by traverseQueryPost
586
593
  // we are now in the top-level (parent is entity) or a non-referred query (parent is query)
587
- traverseQueryPost( query, null, callback );
594
+ traverseQueryPost( query, false, callback ); // not into right of UNION
588
595
  }
589
596
  }
590
597
 
@@ -602,15 +609,18 @@ function viewFromPrimary( view ) {
602
609
  /**
603
610
  * About Helper property $expand for faster the XSN-to-CSN transformation
604
611
  * - null/undefined: artifact, member, items does not contain expanded members
605
- * - 'origin': all expanded (sub) elements have no new target/on and no new annotations
612
+ * - 'origin': all expanded (sub) elements have no associations and no new annotations
606
613
  * that value is only on elements, types, and params -> no other members
607
614
  * when set, only on elem/art with expanded elements
608
- * - 'target': all expanded (sub) elements might only have new target/on, but
615
+ * - 'target': all expanded (sub) elements might only have associations, but
609
616
  * no individual annotations on any (sub) member
610
617
  * when set, traverse all parents where the value has been 'origin' before
618
+ * TODO: only set if `target` or `on` has changed during redirection/rewrite
611
619
  * - 'annotate': at least one inferred (sub) member has an individual annotation,
612
620
  * not counting propagated ones; set up to the definition (main artifact)
613
- * (only set with anno on $inferred elem), annotate beats target
621
+ * (only set with anno on $inferred elem), 'annotate' beats 'target'
622
+ * - 'extend': at least one member has been added, 'extend' beats 'annotate'
623
+ *
614
624
  * Usage according to CSN flavor:
615
625
  * - gensrc: do not render inferred elements (including expanded elements),
616
626
  * collect annotate statements with value 'annotate'
@@ -619,22 +629,16 @@ function viewFromPrimary( view ) {
619
629
  * (might sometimes render the elements unnecessarily, which is not wrong)
620
630
  * - universal: do not render expanded sub elements if $expand = 'origin'
621
631
  */
622
- function setExpandStatus( elem, status ) {
623
- // set on element
624
- while (elem._main) {
625
- elem = elem._parent;
626
- if (status === 'annotate' ? elem.$expand === 'annotate' : elem.$expand !== 'origin')
627
- return;
628
- elem.$expand = status; // meaning: expanded, containing assocs
629
- for (let line = elem.items; line; line = line.items)
630
- line.$expand = status; // to-csn just uses the innermost $expand
631
- }
632
- }
632
+ const expandStatusLevel = {
633
+ origin: 1,
634
+ target: 2,
635
+ annotate: 3,
636
+ extend: 4,
637
+ };
633
638
 
634
- function setExpandStatusAnnotate( elem, status ) {
635
- for (;;) {
636
- if (elem.$expand === status)
637
- return; // already set
639
+ function setExpandStatus( elem, status ) {
640
+ const statusLevel = expandStatusLevel[status];
641
+ while (!elem.$expand || expandStatusLevel[elem.$expand] < statusLevel) {
638
642
  elem.$expand = status; // meaning: expanded, containing annos
639
643
  for (let line = elem.items; line; line = line.items)
640
644
  line.$expand = status; // to-csn just uses the innermost $expand
@@ -862,7 +866,6 @@ module.exports = {
862
866
  traverseQueryExtra,
863
867
  viewFromPrimary,
864
868
  setExpandStatus,
865
- setExpandStatusAnnotate,
866
869
  isDirectComposition,
867
870
  targetCantBeAspect,
868
871
  userQuery,
@@ -148,7 +148,7 @@ const { weakLocation } = require('../base/location');
148
148
  const {
149
149
  setArtifactLink,
150
150
  setLink,
151
- setExpandStatusAnnotate,
151
+ setExpandStatus,
152
152
  } = require('./utils');
153
153
  const { CompilerAssertion } = require('../base/error');
154
154
  const { isBetaEnabled } = require('../base/specialOptions');
@@ -550,7 +550,7 @@ function xprRewriteFns( model ) {
550
550
 
551
551
  stripAbsolutePathPrefix( expr, config.expandedRootType );
552
552
  prependRootPath( config.expandedRootType, config.expandedRoot, expr );
553
- setExpandStatusAnnotate( config.destination, 'annotate' );
553
+ setExpandStatus( config.destination, 'annotate' );
554
554
 
555
555
  config.destination[config.anno].$inferred = 'anno-rewrite';
556
556
  // $self-paths via type expansion always need to be rewritten.
@@ -244,7 +244,10 @@ const EdmPrimitiveTypeMap = {
244
244
  exact: 0,
245
245
  desc: 'Abstract meta type',
246
246
  },
247
- // 'Edm.Untyped': { v4: true, desc: 'Abstract void type' },
247
+ 'Edm.Untyped': {
248
+ v4: true,
249
+ desc: 'Abstract void type',
250
+ },
248
251
  };
249
252
  const EdmPathTypeMap = {
250
253
  'Edm.AnnotationPath': 1,
@@ -891,13 +891,18 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
891
891
  // oTarget: the result object (o: odata)
892
892
  // oTermName: current term
893
893
  // dTypeNameArg: expected type of cAnnoValue according to dictionary, may be null (d: dictionary)
894
- function handleValue( cAnnoValue, oTarget, oTermName, dTypeNameArg, msg ) {
894
+ // dTypeDef: the whole object definition of the typefrom the dictionary, needed for DerivedTypeConstraints for Edm.Untyped
895
+ // @ts-ignore
896
+ function handleValue( cAnnoValue, oTarget, oTermName, dTypeNameArg, msg/* , dTypeDef = undefined */ ) {
895
897
  // this function basically only figures out what kind of annotation value we have
896
898
  // (can be: array, expression, enum, pseudo-record, record, simple value),
897
899
  // then calls a more specific function to deal with it and puts
898
900
  // the result into the oTarget object
899
901
 
900
- const [ dTypeName, dTypeIsACollection ] = stripCollection(dTypeNameArg);
902
+ const [ dTypeName, dTypeIsACollection/* , dTypeIsUntyped */ ] = stripCollectionAndgetIsCollectionOrUntyped(dTypeNameArg);
903
+
904
+ // if (dTypeIsUntyped)
905
+ // validateUntypedAnnotationValue(cAnnoValue, typeof dTypeNameArg === 'object' ? dTypeNameArg : dTypeDef, msg);
901
906
 
902
907
  if (Array.isArray(cAnnoValue)) {
903
908
  if (isEnumType(dTypeName)) {
@@ -915,7 +920,12 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
915
920
  if ('=' in cAnnoValue) {
916
921
  if (dTypeIsACollection) {
917
922
  message('odata-anno-value', msg.location,
918
- { anno: msg.anno(), str: 'path', '#': 'incompval' });
923
+ {
924
+ anno: msg.anno(),
925
+ type: dTypeName,
926
+ str: 'path',
927
+ '#': 'incompval',
928
+ });
919
929
  }
920
930
  // expression
921
931
  const res = handleExpression(cAnnoValue['='], dTypeName);
@@ -1081,6 +1091,30 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1081
1091
  // concatenate all the enums to a string, separated by spaces
1082
1092
  return cAnnoValue.filter( x => x['#']).map( x => (forXml ? `${ dTypeName }/` : '') + x['#'] ).join(forXml ? ' ' : ',');
1083
1093
  }
1094
+
1095
+ // --------------------- Edm.Untyped helper function ----------------------
1096
+
1097
+ // // eslint-disable-next-line no-shadow
1098
+ // function validateUntypedAnnotationValue( cAnnoValue, dTypeDef, msg ) {
1099
+ // if (!dTypeDef?.DerivedTypeConstraint)
1100
+ // return;
1101
+ // // if the annotation value is a path, but no path is allowed for this annotation, issue a message,
1102
+ // // for example the UI.DataField.Value annotation
1103
+ // if (cAnnoValue.$edmJson?.$Path/* || cAnnoValue['=']) -> this is handled in handleValue */ &&
1104
+ // !(dTypeDef.DerivedTypeConstraint.some(type => EdmPathTypeMap[type]) ||
1105
+ // dTypeDef.DerivedTypeConstraint.includes('Edm.EntityType') ||
1106
+ // dTypeDef.DerivedTypeConstraint.includes('Edm.ComplexType'))
1107
+ // ) {
1108
+ // message('odata-anno-value', msg.location,
1109
+ // {
1110
+ // anno: msg.anno(),
1111
+ // str: 'path',
1112
+ // type: 'Edm.Untyped',
1113
+ // '#': 'incompval',
1114
+ // });
1115
+ // }
1116
+ // }
1117
+ // ----------------------------------------------------------------------
1084
1118
  }
1085
1119
 
1086
1120
  // found an expression value ("=") "expr"
@@ -1418,7 +1452,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1418
1452
  function generateCollection( annoValue, termName, dTypeName, dTypeIsACollection, msg ) {
1419
1453
  const newCollection = new Edm.Collection(v);
1420
1454
 
1421
- if (dTypeName && !dTypeIsACollection) {
1455
+ if (dTypeName && !dTypeIsACollection && dTypeName !== 'Edm.Untyped') {
1422
1456
  message('odata-anno-value', msg.location,
1423
1457
  {
1424
1458
  anno: msg.anno(), str: 'collection', type: dTypeName, '#': 'incompval',
@@ -1721,14 +1755,23 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1721
1755
  return dTypeName;
1722
1756
  }
1723
1757
 
1724
- function stripCollection( typeName ) {
1725
- if (typeName) {
1758
+ function stripCollectionAndgetIsCollectionOrUntyped( typeName ) {
1759
+ if (typeName && typeof typeName === 'string') {
1726
1760
  const match = typeName.match(/^Collection\((.+)\)/);
1727
1761
  if (match)
1728
1762
  return [ match[1], true ];
1763
+ // return [ match[1], true, match[1] === 'Edm.Untyped' ];
1729
1764
  }
1730
1765
 
1766
+ // in the Dictionary.json, if a certain element or type has type 'Edm.Untyped', we also store
1767
+ // a 'DerivedTypeConstraint' property containing the possible types for this element or type.
1768
+ // Therefore, the type is not just a string but an object: { Type: 'Edm.Untyped', DerivedTypeConstraint: [...] }
1769
+ if (typeName && typeof typeName === 'object')
1770
+ return [ typeName.Type, typeName.DerivedTypeConstraint.every(type => type.startsWith('Collection(')) ];
1771
+ // return [ typeName.Type, typeName.DerivedTypeConstraint.some(type => type.startsWith('Collection(')), true ];
1772
+
1731
1773
  return [ typeName, false ];
1774
+ // return [ typeName, false, typeName === 'Edm.Untyped' ];
1732
1775
  }
1733
1776
 
1734
1777
  function isPrimitiveType( typeName ) {
@@ -472,8 +472,10 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
472
472
 
473
473
  navigationProperties.forEach((np) => {
474
474
  if (options.isV4()) {
475
- // V4: No referential constraints for Containment Relationships
476
- if ((!np.isContainment() || (options.renderForeignKeys)) && !np.isToMany())
475
+ // V4: No referential constraints for Containment Relationships or
476
+ // unmanaged Compositions without a partner
477
+ const isUnmanagedCompositionWithoutPartner = np._csn.type === 'cds.Composition' && np._csn.on && !np._csn._constraints._partnerCsn;
478
+ if ((!np.isContainment() || (options.renderForeignKeys)) && !np.isToMany() && !isUnmanagedCompositionWithoutPartner)
477
479
  np.addReferentialConstraintNodes();
478
480
  }
479
481
  else {
package/lib/edm/edm.js CHANGED
@@ -1053,11 +1053,14 @@ function getEdm( options ) {
1053
1053
  delete this._edmAttributes.Nullable;
1054
1054
  }
1055
1055
  // we have exactly one selfReference or the default partner
1056
-
1057
- if ( !csn.$noPartner) {
1056
+ // $noPartner can be set when a second projection creates an ambiguous backlink, but if exactly
1057
+ // one self-reference targets the same entity as this association, that unambiguous partner still applies.
1058
+ const selfRefsToTarget = csn._selfReferences.filter(ref => ref.$abspath[0] === csn._target?.name);
1059
+ const isPartner = selfRefsToTarget.length === 1 && selfRefsToTarget[0]._constraints?._partnerCsn === csn;
1060
+ if (!csn.$noPartner || isPartner) {
1058
1061
  const partner = csn._selfReferences.length === 1
1059
1062
  ? csn._selfReferences[0]
1060
- : csn._constraints._partnerCsn;
1063
+ : selfRefsToTarget[0] || csn._constraints._partnerCsn;
1061
1064
  if (partner && partner['@odata.navigable'] !== false && this._csn._edmParentCsn.kind !== 'type') {
1062
1065
  // $abspath[0] is main entity
1063
1066
  this._edmAttributes.Partner = partner.$abspath.slice(1).join('/');
@@ -1,4 +1,4 @@
1
- // Base class for generated parser, for redepage v0.3.4
1
+ // Base class for generated parser, for redepage v0.4.0
2
2
 
3
3
  'use strict';
4
4
 
@@ -24,7 +24,6 @@ recoverTokenIdx = -1;
24
24
  reuseErrorTokenIdx = null;
25
25
  fixKeywordTokenIdx = -1;
26
26
  conditionStackLength = -1;
27
- nextTokenAsId = false;
28
27
 
29
28
  s = null;
30
29
  errorState = null;
@@ -32,7 +31,7 @@ stack = [];
32
31
  dynamic_ = {};
33
32
  prec_ = null;
34
33
  $hasErrors = null;
35
- leanConditions = {};
34
+ hardGuards = {};
36
35
  _trace = [];
37
36
 
38
37
  constructor( lexer, keywords, table ) {
@@ -103,22 +102,30 @@ return this.tokens[this.stack[this.stack.length - 1].tokenIdx];
103
102
 
104
103
 
105
104
 
106
- l() {
105
+ lt() {
107
106
  return this.la().type;
108
107
  }
109
108
 
110
109
 
111
110
  lk() {
112
111
  const la = this.la();
113
- if (!this.nextTokenAsId)
114
112
  return la.keyword || la.type;
113
+ }
115
114
 
116
- this.nextTokenAsId = false;
117
- return la.type;
115
+ l() {
116
+ let cmd = this.table[this.s];
117
+ let { keyword, type } = this.la();
118
+ const kb = keyword && cmd[keyword];
119
+ if (Array.isArray( kb ) &&
120
+ (!kb[2] || this.fixKeywordTokenIdx === this.tokenIdx || this._predictKeyword( kb[2] )))
121
+ return (kb[3] && this._rejectCond( kb[3], kb[4] )) ? '' : keyword;
122
+ const tb = cmd[type];
123
+ return (!tb || tb[3] && this._rejectCond( tb[3], tb[4] )) ? '' : type;
118
124
  }
119
125
 
120
126
  e() {
121
127
  const la = this.la();
128
+ this.s = null;
122
129
 
123
130
  if (this.errorTokenIdx === this.tokenIdx &&
124
131
  (this.reuseErrorTokenIdx == null && this.reuseErrorTokenIdx < this.tokenIdx))
@@ -140,21 +147,11 @@ return false;
140
147
  }
141
148
 
142
149
 
143
- ei() {
144
- if (!this.la().keyword)
145
- return this.e();
146
- this.nextTokenAsId = true;
147
- return false;
148
- }
149
-
150
-
151
150
 
152
151
 
153
152
  gr( follow ) {
154
- if (this.stack[this.stack.length - 1].tokenIdx === this.tokenIdx)
155
-
156
- return this.e();
157
- this.s = 0;
153
+ if (!this.g( 0 ))
154
+ return false;
158
155
 
159
156
 
160
157
  const { type, keyword } = this.tokens[this.tokenIdx];
@@ -172,35 +169,11 @@ return (match ?? true) || this.e();
172
169
  }
173
170
 
174
171
 
175
- g( state, follow ) {
176
- if (!(state == null ? this.e() : state || this.gr( follow )))
177
- return false;
172
+ g( state ) {
178
173
  this.s = state;
179
- return true
180
- }
181
-
182
-
183
- giA( state, follow ) {
184
- if (!this.tokens[this.tokenIdx].keyword)
185
- return this.g( state, follow );
186
- this.nextTokenAsId = true;
187
- return false;
188
- }
189
-
190
-
191
- gi( state, follow ) {
192
- const lk = this.tokens[this.tokenIdx].keyword;
193
-
194
-
195
- if (!lk || this.keywords[lk])
196
- return this.g( state, follow );
197
- this.nextTokenAsId = true;
198
- return false;
199
- }
200
-
201
-
202
- gP( state, follow ) {
203
- return this.lP( follow ) && this.g( state );
174
+ return (state || this.stack[this.stack.length - 1].tokenIdx < this.tokenIdx)
175
+ ? true
176
+ : this.e();
204
177
  }
205
178
 
206
179
 
@@ -219,9 +192,9 @@ return (this.tokens[this.tokenIdx].type === 'Id')
219
192
  }
220
193
 
221
194
 
222
- miA( state, ident = true ) {
195
+ my( state, ident = true ) {
223
196
  return (this.tokens[this.tokenIdx].type === 'Id')
224
- ? this.ciA( state, ident )
197
+ ? this.cy( state, ident )
225
198
  : this.e();
226
199
  }
227
200
 
@@ -247,7 +220,7 @@ return this.e();
247
220
  const la = this.tokens[this.tokenIdx];
248
221
 
249
222
 
250
- if (this.keywords[la.keyword]) {
223
+ if (this.keywords[la.keyword] && this.errorTokenIdx !== this.tokenIdx) {
251
224
 
252
225
  if (this._runTransparently( () => {
253
226
  ++this.tokenIdx;
@@ -264,7 +237,7 @@ return this.c( state, ident )
264
237
  }
265
238
 
266
239
 
267
- ciA( state, ident = 'ident' ) {
240
+ cy( state, ident = 'ident' ) {
268
241
  return this.c( state, ident )
269
242
  }
270
243
 
@@ -274,12 +247,7 @@ return this.c( state, 'keyword' )
274
247
  }
275
248
 
276
249
 
277
- ckP( state, first2 ) {
278
- return this.lP( first2 ) && this.ck( state );
279
- }
280
-
281
-
282
- ckA( state ) {
250
+ cx( state ) {
283
251
 
284
252
  return this.c( state, (this.l() === 'Id' ? 'keyword' : 'token') );
285
253
  }
@@ -295,15 +263,11 @@ reuseToken_() {
295
263
 
296
264
 
297
265
 
298
-
299
-
300
-
301
-
302
- gc( state, cond, arg ) {
266
+ _rejectCond( cond, arg ) {
303
267
  if (this.conditionTokenIdx === this.tokenIdx &&
304
268
  this.conditionStackLength == null &&
305
269
  !this[cond].afterError)
306
- return true
270
+ return false
307
271
 
308
272
 
309
273
 
@@ -314,7 +278,8 @@ const fail = this[cond]( arg, true );
314
278
 
315
279
 
316
280
 
317
- if (fail) {
281
+ if (!fail)
282
+ return false;
318
283
 
319
284
 
320
285
 
@@ -326,17 +291,13 @@ const { keyword } = this.la();
326
291
  if (keyword && this.table[this.s][keyword])
327
292
  this.fixKeywordTokenIdx = this.tokenIdx;
328
293
  this.conditionTokenIdx = this.tokenIdx;
294
+
329
295
  this.conditionStackLength = this.stack.length;
330
296
  this.conditionName = cond;
331
297
 
332
298
 
333
299
  this.conditionFailure = fail;
334
- }
335
- return !fail || this.g( state ) && false;
336
- }
337
-
338
- ec( cond, arg ) {
339
- return this.gc( null, cond, arg );
300
+ return true;
340
301
  }
341
302
 
342
303
 
@@ -382,13 +343,10 @@ return true;
382
343
  }
383
344
 
384
345
 
346
+ _predictKeyword( first2 ) {
385
347
 
386
-
387
-
388
- lP( first2 ) {
389
-
390
- const { keyword: lk1 } = this.tokens[this.tokenIdx];
391
- if (!lk1 || this.keywords[lk1] !== 0 || this.fixKeywordTokenIdx === this.tokenIdx)
348
+ const { keyword: lk1 } = this.la();
349
+ if (this.keywords[lk1] !== 0)
392
350
  return true;
393
351
 
394
352
 
@@ -404,10 +362,7 @@ if (this._walkPred( this.table[this.s][lk1], lk1, lt2, lk2 ))
404
362
  return true;
405
363
 
406
364
  const choice = this.table[this.s];
407
- if (!this._walkPred( choice.Id || choice[''], null, lt2, lk2 ))
408
- return true;
409
- this.nextTokenAsId = true;
410
- return false;
365
+ return !this._walkPred( choice.Id || choice[''], null, lt2, lk2 );
411
366
  }
412
367
 
413
368
  _walkPred( cmd, lk1, lt2, lk2 ) {
@@ -483,9 +438,9 @@ cmd = !(c && this._rejectCondition( c, mode, lean )) && c || cmd[''];
483
438
  const state = this.s;
484
439
  this.s = cmd[1];
485
440
  switch (cmd[0]) {
486
- case 'c': case 'ck': case 'ckA':
441
+ case 'c': case 'ck': case 'cx':
487
442
  return true;
488
- case 'ciA':
443
+ case 'cy':
489
444
  return mode !== 'F';
490
445
 
491
446
 
@@ -494,6 +449,8 @@ case 'ci':
494
449
  if (!keyword ||
495
450
  !this.keywords[keyword] && this.fixKeywordTokenIdx !== this.tokenIdx)
496
451
  return mode !== 'F';
452
+
453
+
497
454
  cmd = this.table[state][''];
498
455
  this.s = cmd[1];
499
456
  break;
@@ -503,7 +460,7 @@ case 'mi':
503
460
  return type === 'Id' && mode !== 'F' &&
504
461
  (!keyword ||
505
462
  !this.keywords[keyword] && this.fixKeywordTokenIdx !== this.tokenIdx);
506
- case 'miA':
463
+ case 'my':
507
464
  return type === 'Id' && mode !== 'F';
508
465
  case 'mk':
509
466
  return keyword === cmd[2];
@@ -553,7 +510,7 @@ return (hasMatchedToken ?? this.tokenIdx > this.stack.at( -1 ).tokenIdx)
553
510
 
554
511
  _rejectCondition( cmd, mode, lean ) {
555
512
  const cond = cmd[3];
556
- if (!cond || lean && !this.leanConditions[cond])
513
+ if (!cond || lean && !this.hardGuards[cond])
557
514
  return false;
558
515
  if (!this.constructor.tracingParser)
559
516
  return !!this[cond]( cmd[4], mode );
@@ -580,9 +537,6 @@ this.s = this.stack[depth].followState;
580
537
 
581
538
  match = this._pred_next( type, keyword, mode );
582
539
 
583
-
584
-
585
-
586
540
  }
587
541
  this.dynamic_ = dynamic_;
588
542
  this.s = savedState;
@@ -660,12 +614,16 @@ cmd ??= this.table[this.s];
660
614
  if (!Array.isArray( cmd )) {
661
615
  const lookahead = cmd[' lookahead'];
662
616
  const dict = cmd;
617
+ cmd = dict[''];
663
618
  for (const prop in dict) {
664
619
  if (prop && Object.hasOwn( dict, prop ) && prop !== 'Id' &&
665
- !Object.hasOwn( expecting, prop ) && prop.charAt(0) !== ' ')
620
+ !Object.hasOwn( expecting, prop ) && prop.charAt(0) !== ' ') {
621
+
622
+ const ad = dict[prop];
623
+ if (ad[1] !== cmd[1] || ad[0] !== 'g')
666
624
  this.addTokenToSet_( expecting, prop, val, collectKeywordsAndIdOnly, lookahead );
667
625
  }
668
- cmd = dict[''];
626
+ }
669
627
  if (dict.Id) {
670
628
 
671
629
  if (cmd[0] === 'e') {
@@ -684,13 +642,13 @@ switch (cmd[0]) {
684
642
  case 'm': case 'mk':
685
643
  this.addTokenToSet_( expecting, cmd[2], val, collectKeywordsAndIdOnly );
686
644
  break loop;
687
- case 'ci': case 'ciA': case 'mi': case 'miA':
645
+ case 'ci': case 'cy': case 'mi': case 'my':
688
646
  this.addTokenToSet_( expecting, 'Id', val, false );
689
647
 
690
648
 
691
649
 
692
650
  break loop;
693
- case 'g': case 'gi': case 'e':
651
+ case 'g': case 'e':
694
652
  break;
695
653
  default:
696
654
  if (typeof cmd[0] !== 'number')
@@ -914,17 +872,21 @@ return table;
914
872
  for (const line of table) {
915
873
  if (typeof line !== 'object' || Array.isArray( line ))
916
874
  continue;
917
- const cache = Object.create( null );
918
- for (const prop of Object.keys( line )) {
919
- const alt = line[prop];
920
- if (!Array.isArray( alt ) && prop.charAt(0) !== ' ')
921
- line[prop] = (typeof alt === 'string') ? line[alt] : (cache[alt] ??= [ 'g', alt ]);
922
- }
875
+ for (const prop of Object.keys( line ))
876
+ compileTableItem( line, prop );
923
877
  if (!line[''])
924
878
  line[''] = [ 'e' ];
925
879
  }
926
880
  table.$compiled = true;
927
881
  return table;
928
882
  }
883
+ function compileTableItem( line, prop ) {
884
+ const alt = line[prop];
885
+ if (typeof alt === 'number')
886
+ return line[prop] = [ 'g', alt ];
887
+ else if (typeof alt === 'string' && prop.charAt(0) !== ' ')
888
+ return line[prop] = compileTableItem( line, alt );
889
+ return alt;
890
+ }
929
891
  BaseParser.prototype.isNoKeywordInRuleFollow.afterError = true;
930
892
  module.exports = BaseParser;
@@ -1 +1 @@
1
- 1802b89cdce8e878cb15384d6731099a
1
+ 43e8ba55fd2a4f645dcbc0928d83528b