@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.
- package/CHANGELOG.md +55 -0
- package/README.md +4 -0
- package/bin/cdshi.js +1 -0
- package/lib/api/main.js +8 -1
- package/lib/api/options.js +3 -1
- package/lib/base/builtins.js +13 -9
- package/lib/base/csnRefs.js +8 -10
- package/lib/base/message-registry.js +61 -3
- package/lib/base/messages.js +2 -0
- package/lib/base/optionProcessor.js +2 -0
- package/lib/base/specialOptions.js +1 -1
- package/lib/compiler/assert-consistency.js +11 -9
- package/lib/compiler/base.js +5 -1
- package/lib/compiler/define.js +1 -1
- package/lib/compiler/dictionaries.js +2 -3
- package/lib/compiler/extend.js +137 -27
- package/lib/compiler/lsp-api.js +3 -3
- package/lib/compiler/populate.js +4 -5
- package/lib/compiler/resolve.js +50 -35
- package/lib/compiler/shared.js +33 -14
- package/lib/compiler/tweak-assocs.js +2 -2
- package/lib/compiler/utils.js +26 -23
- package/lib/compiler/xpr-rewrite.js +2 -2
- package/lib/edm/EdmPrimitiveTypeDefinitions.js +4 -1
- package/lib/edm/annotations/genericTranslation.js +49 -6
- package/lib/edm/csn2edm.js +4 -2
- package/lib/edm/edm.js +6 -3
- package/lib/gen/BaseParser.js +59 -97
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +2055 -1969
- package/lib/gen/Dictionary.json +67 -7
- package/lib/json/from-csn.js +7 -12
- package/lib/json/to-csn.js +59 -35
- package/lib/parsers/AstBuildingParser.js +43 -30
- package/lib/render/toCdl.js +46 -27
- package/lib/render/toSql.js +9 -0
- package/lib/render/utils/common.js +3 -2
- package/lib/tool-lib/enrichCsn.js +1 -0
- package/lib/transform/effective/flattening.js +6 -5
- package/lib/transform/effective/main.js +5 -0
- package/lib/transform/forOdata.js +20 -2
- package/lib/transform/forRelationalDB.js +8 -4
- package/lib/transform/tupleExpansion.js +40 -0
- package/package.json +3 -40
package/lib/compiler/utils.js
CHANGED
|
@@ -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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
|
635
|
-
|
|
636
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ] =
|
|
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
|
-
{
|
|
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
|
|
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 ) {
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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('/');
|
package/lib/gen/BaseParser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Base class for generated parser, for redepage v0.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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.
|
|
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
|
|
176
|
-
if (!(state == null ? this.e() : state || this.gr( follow )))
|
|
177
|
-
return false;
|
|
172
|
+
g( state ) {
|
|
178
173
|
this.s = state;
|
|
179
|
-
return
|
|
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
|
-
|
|
195
|
+
my( state, ident = true ) {
|
|
223
196
|
return (this.tokens[this.tokenIdx].type === 'Id')
|
|
224
|
-
? this.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 '
|
|
441
|
+
case 'c': case 'ck': case 'cx':
|
|
487
442
|
return true;
|
|
488
|
-
case '
|
|
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 '
|
|
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.
|
|
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
|
-
|
|
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 '
|
|
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 '
|
|
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
|
|
918
|
-
|
|
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
|
-
|
|
1
|
+
43e8ba55fd2a4f645dcbc0928d83528b
|