@sap/cds-compiler 5.6.0 → 5.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +2 -1
- package/doc/Versioning.md +4 -4
- package/lib/api/options.js +1 -0
- package/lib/base/builtins.js +2 -2
- package/lib/base/dictionaries.js +1 -2
- package/lib/base/keywords.js +3 -1
- package/lib/base/lazyload.js +1 -1
- package/lib/base/message-registry.js +169 -144
- package/lib/base/messages.js +69 -59
- package/lib/base/model.js +3 -3
- package/lib/base/node-helpers.js +17 -16
- package/lib/base/optionProcessorHelper.js +13 -14
- package/lib/base/shuffle.js +4 -1
- package/lib/checks/structuredAnnoExpressions.js +1 -1
- package/lib/compiler/assert-consistency.js +1 -1
- package/lib/compiler/builtins.js +2 -1
- package/lib/compiler/extend.js +20 -5
- package/lib/compiler/resolve.js +45 -9
- package/lib/compiler/shared.js +1 -0
- package/lib/edm/annotations/edmJson.js +3 -3
- package/lib/edm/annotations/genericTranslation.js +5 -1
- package/lib/edm/annotations/vocabularyDefinitions.js +2 -2
- package/lib/edm/edmUtils.js +2 -1
- package/lib/gen/BaseParser.js +32 -32
- package/lib/gen/CdlParser.js +1526 -1488
- package/lib/json/from-csn.js +2 -0
- package/lib/json/to-csn.js +13 -4
- package/lib/language/docCommentParser.js +11 -5
- package/lib/language/errorStrategy.js +3 -3
- package/lib/language/genericAntlrParser.js +2 -0
- package/lib/model/csnUtils.js +6 -1
- package/lib/optionProcessor.js +5 -1
- package/lib/parsers/AstBuildingParser.js +161 -73
- package/lib/parsers/CdlGrammar.g4 +129 -85
- package/lib/parsers/Lexer.js +5 -3
- package/lib/parsers/index.js +1 -1
- package/lib/render/toCdl.js +6 -5
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +5 -3
- package/lib/render/utils/common.js +19 -6
- package/lib/render/utils/delta.js +1 -3
- package/lib/render/utils/standardDatabaseFunctions.js +576 -0
- package/lib/transform/addTenantFields.js +2 -1
- package/lib/transform/db/flattening.js +18 -77
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/rewriteCalculatedElements.js +14 -19
- package/lib/transform/db/temporal.js +2 -1
- package/lib/transform/odata/adaptAnnotationRefs.js +79 -0
- package/lib/transform/odata/createForeignKeys.js +4 -71
- package/lib/transform/odata/flattening.js +11 -1
- package/lib/transform/transformUtils.js +20 -85
- package/package.json +2 -1
- package/bin/cds_update_annotations.js +0 -180
package/lib/compiler/resolve.js
CHANGED
|
@@ -82,7 +82,7 @@ const expWithFilter = [ 'from', 'expand', 'inline' ];
|
|
|
82
82
|
// exception in case of an error, but push the corresponding error object to
|
|
83
83
|
// that property (should be a vector).
|
|
84
84
|
function resolve( model ) {
|
|
85
|
-
|
|
85
|
+
const { options } = model;
|
|
86
86
|
// Get shared functionality and the message function:
|
|
87
87
|
const {
|
|
88
88
|
info, warning, error, message,
|
|
@@ -90,6 +90,7 @@ function resolve( model ) {
|
|
|
90
90
|
const {
|
|
91
91
|
resolvePath,
|
|
92
92
|
resolveDefinitionName,
|
|
93
|
+
attachAndEmitValidNames,
|
|
93
94
|
traverseExpr,
|
|
94
95
|
effectiveType,
|
|
95
96
|
getOrigin,
|
|
@@ -103,7 +104,7 @@ function resolve( model ) {
|
|
|
103
104
|
} );
|
|
104
105
|
|
|
105
106
|
const ignoreSpecifiedElements
|
|
106
|
-
= isDeprecatedEnabled(
|
|
107
|
+
= isDeprecatedEnabled( options, 'ignoreSpecifiedQueryElements' );
|
|
107
108
|
|
|
108
109
|
return doResolve();
|
|
109
110
|
|
|
@@ -127,7 +128,7 @@ function resolve( model ) {
|
|
|
127
128
|
// Phase 4: resolve all artifacts:
|
|
128
129
|
forEachDefinition( model, resolveRefs );
|
|
129
130
|
forEachGeneric( model, 'vocabularies', resolveRefs );
|
|
130
|
-
if (
|
|
131
|
+
if (options.lspMode) {
|
|
131
132
|
for (const name in model.sources)
|
|
132
133
|
resolveDefinitionName( model.sources[name].namespace );
|
|
133
134
|
}
|
|
@@ -405,7 +406,7 @@ function resolve( model ) {
|
|
|
405
406
|
const allowedInMain = [ 'entity', 'aspect', 'event' ].includes( adHocOrMainKind( art ) );
|
|
406
407
|
const isTopLevelElement = parent && (parent.kind !== 'element' || parent.targetAspect);
|
|
407
408
|
|
|
408
|
-
if (
|
|
409
|
+
if (options.lspMode && art.name && !art._main)
|
|
409
410
|
resolveDefinitionName( art );
|
|
410
411
|
|
|
411
412
|
// Check KEY (TODO: make this an extra function)
|
|
@@ -551,9 +552,11 @@ function resolve( model ) {
|
|
|
551
552
|
addForeignKeyNavigations( art );
|
|
552
553
|
}
|
|
553
554
|
|
|
554
|
-
|
|
555
|
+
resolveExprWithEnum( art.default, 'default', art );
|
|
556
|
+
|
|
555
557
|
// TODO: distinguish not by $syntax (it is semantics), but whether in query
|
|
556
|
-
|
|
558
|
+
const valueCtx = (art.$syntax === 'calc') ? 'calc' : 'column';
|
|
559
|
+
resolveExprWithEnum( art.value, valueCtx, art );
|
|
557
560
|
if (art.type?.$inferred === 'cast')
|
|
558
561
|
inferTypePropertiesFromCast( art );
|
|
559
562
|
if (art.value) {
|
|
@@ -612,7 +615,7 @@ function resolve( model ) {
|
|
|
612
615
|
// If specified type is `null`, type could not be resolved.
|
|
613
616
|
else if (!compToAssoc && sType && sType !== iType &&
|
|
614
617
|
// Special case for $recompilation: allow one level of type indirection. See #12113.
|
|
615
|
-
(!
|
|
618
|
+
(!options.$recompile || sType !== iType.type?._artifact)) {
|
|
616
619
|
const typeName = !iTypeArt && 'typeExtra' || // no inferred type prop
|
|
617
620
|
iType?.name && sType?.name && 'typeName' || // both types are named
|
|
618
621
|
'type'; // unknown type names
|
|
@@ -1391,8 +1394,13 @@ function resolve( model ) {
|
|
|
1391
1394
|
|
|
1392
1395
|
function resolveExprInAnnotations( art ) {
|
|
1393
1396
|
for (const anno in art) {
|
|
1394
|
-
if (anno.charAt(0) === '@')
|
|
1397
|
+
if (anno.charAt(0) === '@') {
|
|
1398
|
+
const { name } = art[anno];
|
|
1399
|
+
const annoDef = model.vocabularies?.[name.id];
|
|
1400
|
+
if (annoDef)
|
|
1401
|
+
setLink( name, '_artifact', annoDef );
|
|
1395
1402
|
resolveAnnoExpr( art[anno], art );
|
|
1403
|
+
}
|
|
1396
1404
|
}
|
|
1397
1405
|
}
|
|
1398
1406
|
|
|
@@ -1400,7 +1408,7 @@ function resolve( model ) {
|
|
|
1400
1408
|
if (expr.$tokenTexts) {
|
|
1401
1409
|
if (!anno.kind)
|
|
1402
1410
|
initAnnotationForExpression( anno, art );
|
|
1403
|
-
|
|
1411
|
+
resolveExprWithEnum( expr, 'annotation', anno );
|
|
1404
1412
|
}
|
|
1405
1413
|
else if (expr.literal === 'array') {
|
|
1406
1414
|
expr.val.forEach( val => resolveAnnoExpr( val, art, anno ) );
|
|
@@ -1428,6 +1436,34 @@ function resolve( model ) {
|
|
|
1428
1436
|
// Might be useful for future recursive types.
|
|
1429
1437
|
}
|
|
1430
1438
|
|
|
1439
|
+
function resolveExprWithEnum( expr, exprCtx, art ) {
|
|
1440
|
+
traverseExpr( expr, exprCtx, art, resolveExprItem );
|
|
1441
|
+
const sym = expr?.sym;
|
|
1442
|
+
const assignment = art;
|
|
1443
|
+
// Check enum as top-level annotation value:
|
|
1444
|
+
if (art?.kind === '$annotation')
|
|
1445
|
+
art = art === expr && art.name._artifact;
|
|
1446
|
+
if (!art)
|
|
1447
|
+
return;
|
|
1448
|
+
const symbols = effectiveType( art )?.enum;
|
|
1449
|
+
if (!sym || !symbols)
|
|
1450
|
+
return;
|
|
1451
|
+
// TODO: or warning if enum symbol but non-enum type?
|
|
1452
|
+
if (symbols[sym.id]) {
|
|
1453
|
+
setLink( sym, '_artifact', symbols[sym.id] );
|
|
1454
|
+
}
|
|
1455
|
+
else {
|
|
1456
|
+
let type = art._effectiveType;
|
|
1457
|
+
// inferred enums can't be extended (yet): show underlying enum
|
|
1458
|
+
while (type.enum[$inferred])
|
|
1459
|
+
type = getOrigin( type );
|
|
1460
|
+
const err = message( 'ref-undefined-enum', [ sym.location, assignment ],
|
|
1461
|
+
{ id: sym.id, type } );
|
|
1462
|
+
if (options.newParser)
|
|
1463
|
+
attachAndEmitValidNames( err, symbols );
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1431
1467
|
function resolveExpr( expr, exprCtx, user ) {
|
|
1432
1468
|
traverseExpr( expr, exprCtx, user, resolveExprItem );
|
|
1433
1469
|
}
|
package/lib/compiler/shared.js
CHANGED
|
@@ -1172,6 +1172,7 @@ function fns( model ) {
|
|
|
1172
1172
|
// TODO: if it becomes non-configurable, we can omit this warning
|
|
1173
1173
|
let id = pathName( path );
|
|
1174
1174
|
let head = path[0]._artifact || { _parent: art };
|
|
1175
|
+
// eslint-disable-next-line sonarjs/no-nested-assignment
|
|
1175
1176
|
while ((head = head?._parent) && head.kind === 'builtin')
|
|
1176
1177
|
id = `${ head.name.id }.${ id }`;
|
|
1177
1178
|
const msgId = (art.$uncheckedElements) ? 'ref-unknown-var' : 'ref-undefined-var';
|
|
@@ -538,8 +538,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
538
538
|
}
|
|
539
539
|
else {
|
|
540
540
|
const facetVal = facetArg.args[0].val;
|
|
541
|
-
const
|
|
542
|
-
if (
|
|
541
|
+
const isNan = Number.isNaN(Number.parseInt(facetVal, 10));
|
|
542
|
+
if (isNan && options.isV4() && facetName === 'Scale' && facetVal !== 'variable') {
|
|
543
543
|
error('odata-anno-xpr-args', location, {
|
|
544
544
|
anno,
|
|
545
545
|
op: `${facetFuncName}(…)`,
|
|
@@ -548,7 +548,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
548
548
|
'#': 'wrongval_meta_list',
|
|
549
549
|
});
|
|
550
550
|
}
|
|
551
|
-
else if (
|
|
551
|
+
else if (isNan && facetName !== 'Scale') {
|
|
552
552
|
error('odata-anno-xpr-args', location, {
|
|
553
553
|
anno, op: `${facetFuncName}(…)`, meta: 'number', '#': 'wrongval_meta',
|
|
554
554
|
});
|
|
@@ -64,7 +64,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
64
64
|
// Crawl over the csn and trigger the annotation translation for all kinds
|
|
65
65
|
// of annotated things.
|
|
66
66
|
// Note: only works for single service
|
|
67
|
-
// Note: we assume that all objects
|
|
67
|
+
// Note: we assume that all objects lie flat in the service, i.e. objName always
|
|
68
68
|
// looks like <service name, can contain dots>.<id>
|
|
69
69
|
forEachDefinition(reqDefs, (def, defName) => {
|
|
70
70
|
if (defName === serviceName || defName.startsWith(`${serviceName}.`)) {
|
|
@@ -1345,6 +1345,10 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1345
1345
|
// if DataFieldAbstract is expected and no explicit type is provided, automatically choose DataField
|
|
1346
1346
|
if (dTypeName === 'UI.DataFieldAbstract')
|
|
1347
1347
|
actualTypeName = 'UI.DataField';
|
|
1348
|
+
// if SemanticObjectMappingAbstract is expected and no explicit type
|
|
1349
|
+
// is provided, automatically choose SemanticObjectMappingType
|
|
1350
|
+
else if (dTypeName === 'Common.SemanticObjectMappingAbstract')
|
|
1351
|
+
actualTypeName = 'Common.SemanticObjectMappingType';
|
|
1348
1352
|
|
|
1349
1353
|
else
|
|
1350
1354
|
actualTypeName = dTypeName;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
|
-
OASIS: https://github.com/oasis-tcs/odata-vocabularies/tree/
|
|
4
|
+
OASIS: https://github.com/oasis-tcs/odata-vocabularies/tree/main/vocabularies
|
|
5
5
|
Aggregation (published)
|
|
6
6
|
Authorization (published)
|
|
7
7
|
Capabilities (published)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
Temporal (published)
|
|
13
13
|
Validation (published)
|
|
14
14
|
|
|
15
|
-
SAP: https://github.com/SAP/odata-vocabularies/tree/
|
|
15
|
+
SAP: https://github.com/SAP/odata-vocabularies/tree/main/vocabularies
|
|
16
16
|
Analytics (published)
|
|
17
17
|
CodeList (published)
|
|
18
18
|
Common (published)
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -866,8 +866,9 @@ function createSchemaRef( serviceRoots, targetSchemaName ) {
|
|
|
866
866
|
function path4( def, _path = def['@path'] ) {
|
|
867
867
|
if (_path)
|
|
868
868
|
return _path.replace(/^\//, '');
|
|
869
|
+
const last = def.name.split('.').at(-1); // > my.very.CatalogService --> CatalogService
|
|
869
870
|
return ( // generate one from the service's name
|
|
870
|
-
|
|
871
|
+
last
|
|
871
872
|
.replace(/Service$/, '') // > CatalogService --> Catalog
|
|
872
873
|
.replace(/([a-z0-9])([A-Z])/g, (_, c, C) => `${c}-${C.toLowerCase()}`) // > ODataFooBarX9 --> odata-foo-bar-x9
|
|
873
874
|
.replace(/_/g, '-') // > foo_bar_baz --> foo-bar-baz
|
package/lib/gen/BaseParser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Base class for generated parser, for redepage v0.1
|
|
1
|
+
// Base class for generated parser, for redepage v0.2.1
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -263,6 +263,8 @@ class BaseParser {
|
|
|
263
263
|
if (this.tokenIdx === this.fixKeywordTokenIdx)
|
|
264
264
|
return this.e();
|
|
265
265
|
const la = this.tokens[this.tokenIdx];
|
|
266
|
+
// TODO: consider this like a failed condition? Will be relevant if we try
|
|
267
|
+
// different error recovery possibilities.
|
|
266
268
|
if (this.keywords[la.keyword])
|
|
267
269
|
this.reportReservedWord_();
|
|
268
270
|
// with error recovery: use that (consider this having a good score)
|
|
@@ -314,7 +316,7 @@ class BaseParser {
|
|
|
314
316
|
}
|
|
315
317
|
// calling the condition might have side effects (precendence conditions have)
|
|
316
318
|
// → call tracing “name” before
|
|
317
|
-
const fail =
|
|
319
|
+
const fail = this[cond]( true, arg );
|
|
318
320
|
if (this.constructor.tracingParser)
|
|
319
321
|
this._traceSubPush( !fail );
|
|
320
322
|
// The default case must not have actions. If written in grammar with action,
|
|
@@ -334,6 +336,10 @@ class BaseParser {
|
|
|
334
336
|
this.fixKeywordTokenIdx = this.tokenIdx;
|
|
335
337
|
this.conditionTokenIdx = this.tokenIdx;
|
|
336
338
|
this.conditionStackLength = this.stack.length;
|
|
339
|
+
this.conditionName = cond;
|
|
340
|
+
// we also set the failure here, because the reporting might have a
|
|
341
|
+
// different context (consider immediate exit)
|
|
342
|
+
this.conditionFailure = fail;
|
|
337
343
|
}
|
|
338
344
|
return !fail || this.g( state ) && false;
|
|
339
345
|
}
|
|
@@ -558,7 +564,7 @@ class BaseParser {
|
|
|
558
564
|
if (!cond || lean && !this.leanConditions[cond])
|
|
559
565
|
return false;
|
|
560
566
|
if (!this.constructor.tracingParser)
|
|
561
|
-
return
|
|
567
|
+
return !!this[cond]( mode, cmd[4] );
|
|
562
568
|
// TODO: let this[cond]( true ) return recovery badness in error case
|
|
563
569
|
if (!lean) {
|
|
564
570
|
const { traceName } = this[cond];
|
|
@@ -566,9 +572,9 @@ class BaseParser {
|
|
|
566
572
|
// calling the condition might have side effects (precendence conditions have)
|
|
567
573
|
// → call tracing “name” before
|
|
568
574
|
}
|
|
569
|
-
const
|
|
570
|
-
this._traceSubPush( lean ? { true: 'C✔', false: 'C✖' }[
|
|
571
|
-
return
|
|
575
|
+
const succeed = !this[cond]( mode, cmd[4] );
|
|
576
|
+
this._traceSubPush( lean ? { true: 'C✔', false: 'C✖' }[succeed] : succeed );
|
|
577
|
+
return !succeed;
|
|
572
578
|
}
|
|
573
579
|
|
|
574
580
|
_matchesInFollow( type, keyword, mode ) { // mode = E | R
|
|
@@ -658,7 +664,7 @@ class BaseParser {
|
|
|
658
664
|
// ( <prefer, guard=fail> 'foo' | rule ) with
|
|
659
665
|
// rule : 'foo' | Id ;
|
|
660
666
|
// doing it already here would list `foo` as expected token
|
|
661
|
-
_tokenSetInRule( expecting, val, cmd ) {
|
|
667
|
+
_tokenSetInRule( expecting, val, cmd, collectKeywordsAndIdOnly = false ) {
|
|
662
668
|
const savedDynamic = this.dynamic_;
|
|
663
669
|
const savedState = this.s;
|
|
664
670
|
let enteredRules = 0;
|
|
@@ -669,27 +675,19 @@ class BaseParser {
|
|
|
669
675
|
const dict = cmd;
|
|
670
676
|
for (const prop in dict) {
|
|
671
677
|
if (prop && Object.hasOwn( dict, prop ) && prop !== 'Id' &&
|
|
672
|
-
!Object.hasOwn( expecting, prop ) && prop.charAt(0) !== ' ')
|
|
673
|
-
|
|
674
|
-
// it should then directly set the dictionary -> setTokenInSet_()
|
|
675
|
-
if (lookahead) { // yes, independently from ckA()
|
|
676
|
-
for (const p of this.translateParserToken_( prop, lookahead ))
|
|
677
|
-
expecting[p] = val;
|
|
678
|
-
}
|
|
679
|
-
else {
|
|
680
|
-
expecting[prop] = val;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
678
|
+
!Object.hasOwn( expecting, prop ) && prop.charAt(0) !== ' ')
|
|
679
|
+
this.addTokenToSet_( expecting, prop, val, collectKeywordsAndIdOnly, lookahead );
|
|
683
680
|
}
|
|
684
681
|
cmd = dict[''];
|
|
685
682
|
if (dict.Id) {
|
|
686
683
|
// recursive call only if Id branch with non-error default branch
|
|
687
684
|
if (cmd[0] === 'e') {
|
|
685
|
+
collectKeywordsAndIdOnly = true;
|
|
688
686
|
cmd = dict.Id;
|
|
689
687
|
}
|
|
690
688
|
else { // Id branch never leads to rule exit:
|
|
691
689
|
this._tracePush( [ '[' ] );
|
|
692
|
-
this._tokenSetInRule( expecting, val, dict.Id );
|
|
690
|
+
this._tokenSetInRule( expecting, val, dict.Id, true );
|
|
693
691
|
this._tracePush( [ ']' ] );
|
|
694
692
|
}
|
|
695
693
|
}
|
|
@@ -697,10 +695,10 @@ class BaseParser {
|
|
|
697
695
|
this._traceSubPush( this.s );
|
|
698
696
|
switch (cmd[0]) {
|
|
699
697
|
case 'm': case 'mk':
|
|
700
|
-
expecting
|
|
698
|
+
this.addTokenToSet_( expecting, cmd[2], val, collectKeywordsAndIdOnly );
|
|
701
699
|
break loop;
|
|
702
700
|
case 'ci': case 'ciA': case 'mi': case 'miA':
|
|
703
|
-
expecting
|
|
701
|
+
this.addTokenToSet_( expecting, 'Id', val, false );
|
|
704
702
|
// TODO: should we do s/th special, such that a reserved word is a sync
|
|
705
703
|
// token for Id<all>? Probably not, see also comment in
|
|
706
704
|
// _findSyncToken()
|
|
@@ -731,8 +729,10 @@ class BaseParser {
|
|
|
731
729
|
return inspectOuterRules;
|
|
732
730
|
}
|
|
733
731
|
|
|
734
|
-
|
|
735
|
-
|
|
732
|
+
// Remark: when called for `Id` token, `collectKeywordsOnly` is `false`
|
|
733
|
+
addTokenToSet_( set, token, val, collectKeywordsOnly, _lookahead ) {
|
|
734
|
+
if (!collectKeywordsOnly || /^[_a-z]/.test( token ))
|
|
735
|
+
set[token] ??= val;
|
|
736
736
|
}
|
|
737
737
|
|
|
738
738
|
// Error reporting and recovery -----------------------------------------------
|
|
@@ -924,37 +924,37 @@ class BaseParser {
|
|
|
924
924
|
// Predefined conditions with extra option names:
|
|
925
925
|
|
|
926
926
|
hide_( mode ) {
|
|
927
|
-
return mode
|
|
927
|
+
return mode === 'M';
|
|
928
928
|
}
|
|
929
929
|
precLeft_( _test, prec ) { // <prec=…>, <…,assoc=left>, <…,prefix=once>
|
|
930
930
|
const parentPrec = this.stack.at( -1 ).prec;
|
|
931
931
|
if (parentPrec != null && parentPrec >= prec)
|
|
932
|
-
return
|
|
932
|
+
return true;
|
|
933
933
|
this.prec_ = prec;
|
|
934
|
-
return
|
|
934
|
+
return false;
|
|
935
935
|
}
|
|
936
936
|
precRight_( _test, prec ) { // <…,assoc=right>, <…,prefix>
|
|
937
937
|
const parentPrec = this.stack.at( -1 ).prec;
|
|
938
938
|
if (parentPrec != null && parentPrec >= prec)
|
|
939
|
-
return
|
|
939
|
+
return true;
|
|
940
940
|
this.prec_ = prec - 1;
|
|
941
|
-
return
|
|
941
|
+
return false;
|
|
942
942
|
}
|
|
943
943
|
precNone_( _test, prec ) { // <…,assoc=none>, <…,postfix=once>
|
|
944
944
|
const parentPrec = this.stack.at( -1 ).prec;
|
|
945
945
|
if (parentPrec != null && parentPrec >= prec ||
|
|
946
946
|
this.prec_ != null && this.prec_ <= prec)
|
|
947
|
-
return
|
|
947
|
+
return true;
|
|
948
948
|
this.prec_ = prec;
|
|
949
|
-
return
|
|
949
|
+
return false;
|
|
950
950
|
}
|
|
951
951
|
precPost_( _test, prec ) { // <…,postfix>
|
|
952
952
|
const parentPrec = this.stack.at( -1 ).prec;
|
|
953
953
|
if (parentPrec != null && parentPrec >= prec ||
|
|
954
954
|
this.prec_ != null && this.prec_ < prec)
|
|
955
|
-
return
|
|
955
|
+
return true;
|
|
956
956
|
this.prec_ = prec;
|
|
957
|
-
return
|
|
957
|
+
return false;
|
|
958
958
|
}
|
|
959
959
|
}
|
|
960
960
|
const members = BaseParser.prototype;
|