@optique/core 1.0.4 → 1.0.6
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/dist/constructs.cjs +155 -58
- package/dist/constructs.js +155 -58
- package/package.json +1 -1
package/dist/constructs.cjs
CHANGED
|
@@ -516,6 +516,35 @@ function createExclusiveState(parentState, index, parser, result) {
|
|
|
516
516
|
return require_annotations.annotateFreshArray(parentState, [index, annotatedResult]);
|
|
517
517
|
}
|
|
518
518
|
/**
|
|
519
|
+
* Detects a branch result that handled only the global options terminator.
|
|
520
|
+
* Such a result advances the shared parser context, but does not identify
|
|
521
|
+
* which exclusive branch matched.
|
|
522
|
+
* @internal
|
|
523
|
+
*/
|
|
524
|
+
function isOptionsTerminatorOnlyResult(context, result) {
|
|
525
|
+
return !context.optionsTerminated && context.buffer[0] === "--" && result.success && result.next.optionsTerminated && result.consumed.length === 1 && result.consumed[0] === "--";
|
|
526
|
+
}
|
|
527
|
+
function isUnselectedBooleanOptionState(state) {
|
|
528
|
+
return state != null && typeof state === "object" && "success" in state && state.success === true && "value" in state && state.value === false;
|
|
529
|
+
}
|
|
530
|
+
function preserveExclusiveStateAfterOptionsTerminator(context, result) {
|
|
531
|
+
const mergedExec = require_execution_context.mergeChildExec(context.exec, result.next.exec);
|
|
532
|
+
return {
|
|
533
|
+
success: true,
|
|
534
|
+
next: {
|
|
535
|
+
...context,
|
|
536
|
+
buffer: result.next.buffer,
|
|
537
|
+
optionsTerminated: true,
|
|
538
|
+
state: context.state,
|
|
539
|
+
...mergedExec != null ? {
|
|
540
|
+
exec: mergedExec,
|
|
541
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
542
|
+
} : {}
|
|
543
|
+
},
|
|
544
|
+
consumed: result.consumed
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
519
548
|
* Creates a complete() method shared by or() and longestMatch().
|
|
520
549
|
* @internal
|
|
521
550
|
*/
|
|
@@ -752,9 +781,15 @@ function or(...args) {
|
|
|
752
781
|
let zeroConsumedBranch = null;
|
|
753
782
|
let zeroConsumedCount = 0;
|
|
754
783
|
let provisionalConsuming = null;
|
|
784
|
+
let optionsTerminatorResult = null;
|
|
755
785
|
let provisionalAmbiguous = false;
|
|
756
786
|
for (const [parser, i] of orderedParsers) {
|
|
757
787
|
const result = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
|
|
788
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
789
|
+
if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
|
|
790
|
+
optionsTerminatorResult ??= result;
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
758
793
|
if (result.success && result.consumed.length > 0) {
|
|
759
794
|
if (result.provisional) {
|
|
760
795
|
const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
|
|
@@ -935,6 +970,7 @@ function or(...args) {
|
|
|
935
970
|
}
|
|
936
971
|
}
|
|
937
972
|
}
|
|
973
|
+
if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
|
|
938
974
|
return {
|
|
939
975
|
...error,
|
|
940
976
|
success: false
|
|
@@ -948,10 +984,16 @@ function or(...args) {
|
|
|
948
984
|
let zeroConsumedBranch = null;
|
|
949
985
|
let zeroConsumedCount = 0;
|
|
950
986
|
let provisionalConsuming = null;
|
|
987
|
+
let optionsTerminatorResult = null;
|
|
951
988
|
let provisionalAmbiguous = false;
|
|
952
989
|
for (const [parser, i] of orderedParsers) {
|
|
953
990
|
const resultOrPromise = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
|
|
954
991
|
const result = await resultOrPromise;
|
|
992
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
993
|
+
if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
|
|
994
|
+
optionsTerminatorResult ??= result;
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
955
997
|
if (result.success && result.consumed.length > 0) {
|
|
956
998
|
if (result.provisional) {
|
|
957
999
|
const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
|
|
@@ -1134,6 +1176,7 @@ function or(...args) {
|
|
|
1134
1176
|
}
|
|
1135
1177
|
}
|
|
1136
1178
|
}
|
|
1179
|
+
if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
|
|
1137
1180
|
return {
|
|
1138
1181
|
...error,
|
|
1139
1182
|
success: false
|
|
@@ -1221,82 +1264,122 @@ function longestMatch(...args) {
|
|
|
1221
1264
|
return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
|
|
1222
1265
|
})()
|
|
1223
1266
|
});
|
|
1224
|
-
const
|
|
1267
|
+
const commitLongestMatch = (context, match, optionsTerminatorResult) => {
|
|
1268
|
+
const selectedResult = optionsTerminatorResult == null ? match.result : {
|
|
1269
|
+
...match.result,
|
|
1270
|
+
next: {
|
|
1271
|
+
...match.result.next,
|
|
1272
|
+
buffer: optionsTerminatorResult.next.buffer,
|
|
1273
|
+
optionsTerminated: true
|
|
1274
|
+
},
|
|
1275
|
+
consumed: [...optionsTerminatorResult.consumed, ...match.result.consumed]
|
|
1276
|
+
};
|
|
1277
|
+
const parser = parsers[match.index];
|
|
1278
|
+
const mergedExec = require_execution_context.mergeChildExec(context.exec, selectedResult.next.exec);
|
|
1279
|
+
return {
|
|
1280
|
+
success: true,
|
|
1281
|
+
next: {
|
|
1282
|
+
...context,
|
|
1283
|
+
buffer: selectedResult.next.buffer,
|
|
1284
|
+
optionsTerminated: selectedResult.next.optionsTerminated,
|
|
1285
|
+
state: createExclusiveState(context.state, match.index, parser, selectedResult),
|
|
1286
|
+
...mergedExec != null ? {
|
|
1287
|
+
exec: mergedExec,
|
|
1288
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1289
|
+
} : {}
|
|
1290
|
+
},
|
|
1291
|
+
consumed: selectedResult.consumed
|
|
1292
|
+
};
|
|
1293
|
+
};
|
|
1294
|
+
const resolveLongestMatchOptionsTerminator = (context, bestMatch, optionsTerminatorMatches) => {
|
|
1295
|
+
if (optionsTerminatorMatches.length < 1) return null;
|
|
1296
|
+
if (bestMatch != null && bestMatch.consumed > 0) return null;
|
|
1297
|
+
const activeState = normalizeExclusiveState(context.state);
|
|
1298
|
+
if (activeState != null && activeState[1].success && activeState[1].consumed.length > 0) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
|
|
1299
|
+
let selected = bestMatch == null ? null : {
|
|
1300
|
+
match: bestMatch,
|
|
1301
|
+
fromTerminator: false
|
|
1302
|
+
};
|
|
1303
|
+
for (const match of optionsTerminatorMatches) {
|
|
1304
|
+
if (!match.selectedAfterTerminator && (extractRequiredUsage(parsers[match.index].usage).length > 0 || isUnselectedBooleanOptionState(match.result.next.state))) continue;
|
|
1305
|
+
const selectedIsProvisional = selected != null && !!selected.match.result.provisional;
|
|
1306
|
+
const matchIsProvisional = !!match.result.provisional;
|
|
1307
|
+
if (selected == null || selectedIsProvisional && !matchIsProvisional || selectedIsProvisional === matchIsProvisional && match.index < selected.match.index) selected = {
|
|
1308
|
+
match,
|
|
1309
|
+
fromTerminator: true
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
if (selected != null) return commitLongestMatch(context, selected.match, selected.fromTerminator ? void 0 : optionsTerminatorMatches[0].result);
|
|
1313
|
+
return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
|
|
1314
|
+
};
|
|
1315
|
+
const parseSyncBranches = (context) => {
|
|
1225
1316
|
let bestMatch = null;
|
|
1317
|
+
const optionsTerminatorMatches = [];
|
|
1226
1318
|
let error = getInitialError(context);
|
|
1227
1319
|
const activeState = normalizeExclusiveState(context.state);
|
|
1228
1320
|
for (let i = 0; i < syncParsers.length; i++) {
|
|
1229
1321
|
const parser = syncParsers[i];
|
|
1230
|
-
const
|
|
1322
|
+
const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
|
|
1323
|
+
const result = parser.parse(childContext);
|
|
1324
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
1325
|
+
optionsTerminatorMatches.push({
|
|
1326
|
+
index: i,
|
|
1327
|
+
result,
|
|
1328
|
+
consumed: 1,
|
|
1329
|
+
...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
|
|
1330
|
+
});
|
|
1331
|
+
continue;
|
|
1332
|
+
}
|
|
1231
1333
|
if (result.success) {
|
|
1232
1334
|
const consumed = context.buffer.length - result.next.buffer.length;
|
|
1233
1335
|
const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
|
|
1234
1336
|
if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
|
|
1235
1337
|
index: i,
|
|
1236
|
-
parser,
|
|
1237
1338
|
result,
|
|
1238
1339
|
consumed
|
|
1239
1340
|
};
|
|
1240
1341
|
} else if (error.consumed < result.consumed) error = result;
|
|
1241
1342
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
success: true,
|
|
1246
|
-
next: {
|
|
1247
|
-
...context,
|
|
1248
|
-
buffer: bestMatch.result.next.buffer,
|
|
1249
|
-
optionsTerminated: bestMatch.result.next.optionsTerminated,
|
|
1250
|
-
state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
|
|
1251
|
-
...mergedExec != null ? {
|
|
1252
|
-
exec: mergedExec,
|
|
1253
|
-
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1254
|
-
} : {}
|
|
1255
|
-
},
|
|
1256
|
-
consumed: bestMatch.result.consumed
|
|
1257
|
-
};
|
|
1258
|
-
}
|
|
1343
|
+
const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
|
|
1344
|
+
if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
|
|
1345
|
+
if (bestMatch != null) return commitLongestMatch(context, bestMatch);
|
|
1259
1346
|
return {
|
|
1260
1347
|
...error,
|
|
1261
1348
|
success: false
|
|
1262
1349
|
};
|
|
1263
1350
|
};
|
|
1264
|
-
const
|
|
1351
|
+
const parseAsyncBranches = async (context) => {
|
|
1265
1352
|
let bestMatch = null;
|
|
1353
|
+
const optionsTerminatorMatches = [];
|
|
1266
1354
|
let error = getInitialError(context);
|
|
1267
1355
|
const activeState = normalizeExclusiveState(context.state);
|
|
1268
1356
|
for (let i = 0; i < parsers.length; i++) {
|
|
1269
1357
|
const parser = parsers[i];
|
|
1270
|
-
const
|
|
1358
|
+
const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
|
|
1359
|
+
const resultOrPromise = parser.parse(childContext);
|
|
1271
1360
|
const result = await resultOrPromise;
|
|
1361
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
1362
|
+
optionsTerminatorMatches.push({
|
|
1363
|
+
index: i,
|
|
1364
|
+
result,
|
|
1365
|
+
consumed: 1,
|
|
1366
|
+
...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
|
|
1367
|
+
});
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1272
1370
|
if (result.success) {
|
|
1273
1371
|
const consumed = context.buffer.length - result.next.buffer.length;
|
|
1274
1372
|
const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
|
|
1275
1373
|
if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
|
|
1276
1374
|
index: i,
|
|
1277
|
-
parser,
|
|
1278
1375
|
result,
|
|
1279
1376
|
consumed
|
|
1280
1377
|
};
|
|
1281
1378
|
} else if (error.consumed < result.consumed) error = result;
|
|
1282
1379
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
success: true,
|
|
1287
|
-
next: {
|
|
1288
|
-
...context,
|
|
1289
|
-
buffer: bestMatch.result.next.buffer,
|
|
1290
|
-
optionsTerminated: bestMatch.result.next.optionsTerminated,
|
|
1291
|
-
state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
|
|
1292
|
-
...mergedExec != null ? {
|
|
1293
|
-
exec: mergedExec,
|
|
1294
|
-
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1295
|
-
} : {}
|
|
1296
|
-
},
|
|
1297
|
-
consumed: bestMatch.result.consumed
|
|
1298
|
-
};
|
|
1299
|
-
}
|
|
1380
|
+
const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
|
|
1381
|
+
if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
|
|
1382
|
+
if (bestMatch != null) return commitLongestMatch(context, bestMatch);
|
|
1300
1383
|
return {
|
|
1301
1384
|
...error,
|
|
1302
1385
|
success: false
|
|
@@ -1319,7 +1402,7 @@ function longestMatch(...args) {
|
|
|
1319
1402
|
return extractExclusivePhase2Seed(parsers, state, exec, combinedMode);
|
|
1320
1403
|
},
|
|
1321
1404
|
parse(context) {
|
|
1322
|
-
return require_mode_dispatch.dispatchByMode(combinedMode, () =>
|
|
1405
|
+
return require_mode_dispatch.dispatchByMode(combinedMode, () => parseSyncBranches(context), () => parseAsyncBranches(context));
|
|
1323
1406
|
},
|
|
1324
1407
|
getSuggestRuntimeNodes(state, path) {
|
|
1325
1408
|
return getExclusiveSuggestRuntimeNodes(parsers, state, path);
|
|
@@ -1365,6 +1448,33 @@ function longestMatch(...args) {
|
|
|
1365
1448
|
return multiResult;
|
|
1366
1449
|
}
|
|
1367
1450
|
/**
|
|
1451
|
+
* Creates the initial parse error shared by object-like combinators.
|
|
1452
|
+
* @param context The current parser context.
|
|
1453
|
+
* @param noMatchContext The kinds of input accepted by the combinator.
|
|
1454
|
+
* @param errors Optional custom error formatters.
|
|
1455
|
+
* @returns A zero-consumption parse error.
|
|
1456
|
+
*/
|
|
1457
|
+
function createObjectLikeInitialError(context, noMatchContext, errors) {
|
|
1458
|
+
if (context.buffer.length < 1) {
|
|
1459
|
+
const customEndOfInput = errors?.endOfInput;
|
|
1460
|
+
return {
|
|
1461
|
+
consumed: 0,
|
|
1462
|
+
error: customEndOfInput ? typeof customEndOfInput === "function" ? customEndOfInput(noMatchContext) : customEndOfInput : generateNoMatchError(noMatchContext)
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
const token = context.buffer[0];
|
|
1466
|
+
const customMessage = errors?.unexpectedInput;
|
|
1467
|
+
if (customMessage) return {
|
|
1468
|
+
consumed: 0,
|
|
1469
|
+
error: typeof customMessage === "function" ? customMessage(token) : customMessage
|
|
1470
|
+
};
|
|
1471
|
+
const baseError = require_message.message`Unexpected option or argument: ${token}.`;
|
|
1472
|
+
return {
|
|
1473
|
+
consumed: 0,
|
|
1474
|
+
error: require_suggestion.createErrorWithSuggestions(baseError, token, context.usage, "both", errors?.suggestions)
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1368
1478
|
* Internal sync helper for object suggest functionality.
|
|
1369
1479
|
* @internal
|
|
1370
1480
|
*/
|
|
@@ -1756,19 +1866,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
1756
1866
|
if (!options.allowDuplicates) checkDuplicateOptionNames(parserPairs.map(([field, parser]) => [field, parser.usage]));
|
|
1757
1867
|
const noMatchContext = analyzeNoMatchContext(parserKeys.map((k) => parsers[k]));
|
|
1758
1868
|
const combinedMode = parserKeys.some((k) => parsers[k].mode === "async") ? "async" : "sync";
|
|
1759
|
-
const getInitialError = (context) => (
|
|
1760
|
-
consumed: 0,
|
|
1761
|
-
error: context.buffer.length > 0 ? (() => {
|
|
1762
|
-
const token = context.buffer[0];
|
|
1763
|
-
const customMessage = options.errors?.unexpectedInput;
|
|
1764
|
-
if (customMessage) return typeof customMessage === "function" ? customMessage(token) : customMessage;
|
|
1765
|
-
const baseError = require_message.message`Unexpected option or argument: ${token}.`;
|
|
1766
|
-
return require_suggestion.createErrorWithSuggestions(baseError, token, context.usage, "both", options.errors?.suggestions);
|
|
1767
|
-
})() : (() => {
|
|
1768
|
-
const customEndOfInput = options.errors?.endOfInput;
|
|
1769
|
-
return customEndOfInput ? typeof customEndOfInput === "function" ? customEndOfInput(noMatchContext) : customEndOfInput : generateNoMatchError(noMatchContext);
|
|
1770
|
-
})()
|
|
1771
|
-
});
|
|
1869
|
+
const getInitialError = (context) => createObjectLikeInitialError(context, noMatchContext, options.errors);
|
|
1772
1870
|
const parseSync = (context) => {
|
|
1773
1871
|
let error = getInitialError(context);
|
|
1774
1872
|
let currentContext = context;
|
|
@@ -2917,6 +3015,7 @@ function merge(...args) {
|
|
|
2917
3015
|
const syncSorted = syncWithIndex.toSorted(([a], [b]) => b.priority - a.priority);
|
|
2918
3016
|
const syncParsers = syncSorted.map(([p]) => p);
|
|
2919
3017
|
if (!options.allowDuplicates) checkDuplicateOptionNames(sorted.map(([parser, originalIndex]) => [String(originalIndex), parser.usage]));
|
|
3018
|
+
const noMatchContext = analyzeNoMatchContext(rawParsers);
|
|
2920
3019
|
const mergedFieldParsers = collectChildFieldParsers(parsers);
|
|
2921
3020
|
const duplicateOutputFieldNames = collectDuplicateFieldNames(mergedFieldParsers);
|
|
2922
3021
|
const parserStateKey = (index) => `__parser_${index}`;
|
|
@@ -3009,8 +3108,7 @@ function merge(...args) {
|
|
|
3009
3108
|
};
|
|
3010
3109
|
return {
|
|
3011
3110
|
success: false,
|
|
3012
|
-
|
|
3013
|
-
error: require_message.message`No matching option or argument found.`
|
|
3111
|
+
...createObjectLikeInitialError(context, noMatchContext)
|
|
3014
3112
|
};
|
|
3015
3113
|
};
|
|
3016
3114
|
const parseAsync = async (context) => {
|
|
@@ -3055,8 +3153,7 @@ function merge(...args) {
|
|
|
3055
3153
|
};
|
|
3056
3154
|
return {
|
|
3057
3155
|
success: false,
|
|
3058
|
-
|
|
3059
|
-
error: require_message.message`No matching option or argument found.`
|
|
3156
|
+
...createObjectLikeInitialError(context, noMatchContext)
|
|
3060
3157
|
};
|
|
3061
3158
|
};
|
|
3062
3159
|
const mergeParser = {
|
package/dist/constructs.js
CHANGED
|
@@ -516,6 +516,35 @@ function createExclusiveState(parentState, index, parser, result) {
|
|
|
516
516
|
return annotateFreshArray(parentState, [index, annotatedResult]);
|
|
517
517
|
}
|
|
518
518
|
/**
|
|
519
|
+
* Detects a branch result that handled only the global options terminator.
|
|
520
|
+
* Such a result advances the shared parser context, but does not identify
|
|
521
|
+
* which exclusive branch matched.
|
|
522
|
+
* @internal
|
|
523
|
+
*/
|
|
524
|
+
function isOptionsTerminatorOnlyResult(context, result) {
|
|
525
|
+
return !context.optionsTerminated && context.buffer[0] === "--" && result.success && result.next.optionsTerminated && result.consumed.length === 1 && result.consumed[0] === "--";
|
|
526
|
+
}
|
|
527
|
+
function isUnselectedBooleanOptionState(state) {
|
|
528
|
+
return state != null && typeof state === "object" && "success" in state && state.success === true && "value" in state && state.value === false;
|
|
529
|
+
}
|
|
530
|
+
function preserveExclusiveStateAfterOptionsTerminator(context, result) {
|
|
531
|
+
const mergedExec = mergeChildExec(context.exec, result.next.exec);
|
|
532
|
+
return {
|
|
533
|
+
success: true,
|
|
534
|
+
next: {
|
|
535
|
+
...context,
|
|
536
|
+
buffer: result.next.buffer,
|
|
537
|
+
optionsTerminated: true,
|
|
538
|
+
state: context.state,
|
|
539
|
+
...mergedExec != null ? {
|
|
540
|
+
exec: mergedExec,
|
|
541
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
542
|
+
} : {}
|
|
543
|
+
},
|
|
544
|
+
consumed: result.consumed
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
519
548
|
* Creates a complete() method shared by or() and longestMatch().
|
|
520
549
|
* @internal
|
|
521
550
|
*/
|
|
@@ -752,9 +781,15 @@ function or(...args) {
|
|
|
752
781
|
let zeroConsumedBranch = null;
|
|
753
782
|
let zeroConsumedCount = 0;
|
|
754
783
|
let provisionalConsuming = null;
|
|
784
|
+
let optionsTerminatorResult = null;
|
|
755
785
|
let provisionalAmbiguous = false;
|
|
756
786
|
for (const [parser, i] of orderedParsers) {
|
|
757
787
|
const result = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
|
|
788
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
789
|
+
if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
|
|
790
|
+
optionsTerminatorResult ??= result;
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
758
793
|
if (result.success && result.consumed.length > 0) {
|
|
759
794
|
if (result.provisional) {
|
|
760
795
|
const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
|
|
@@ -935,6 +970,7 @@ function or(...args) {
|
|
|
935
970
|
}
|
|
936
971
|
}
|
|
937
972
|
}
|
|
973
|
+
if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
|
|
938
974
|
return {
|
|
939
975
|
...error,
|
|
940
976
|
success: false
|
|
@@ -948,10 +984,16 @@ function or(...args) {
|
|
|
948
984
|
let zeroConsumedBranch = null;
|
|
949
985
|
let zeroConsumedCount = 0;
|
|
950
986
|
let provisionalConsuming = null;
|
|
987
|
+
let optionsTerminatorResult = null;
|
|
951
988
|
let provisionalAmbiguous = false;
|
|
952
989
|
for (const [parser, i] of orderedParsers) {
|
|
953
990
|
const resultOrPromise = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
|
|
954
991
|
const result = await resultOrPromise;
|
|
992
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
993
|
+
if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
|
|
994
|
+
optionsTerminatorResult ??= result;
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
955
997
|
if (result.success && result.consumed.length > 0) {
|
|
956
998
|
if (result.provisional) {
|
|
957
999
|
const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
|
|
@@ -1134,6 +1176,7 @@ function or(...args) {
|
|
|
1134
1176
|
}
|
|
1135
1177
|
}
|
|
1136
1178
|
}
|
|
1179
|
+
if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
|
|
1137
1180
|
return {
|
|
1138
1181
|
...error,
|
|
1139
1182
|
success: false
|
|
@@ -1221,82 +1264,122 @@ function longestMatch(...args) {
|
|
|
1221
1264
|
return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
|
|
1222
1265
|
})()
|
|
1223
1266
|
});
|
|
1224
|
-
const
|
|
1267
|
+
const commitLongestMatch = (context, match, optionsTerminatorResult) => {
|
|
1268
|
+
const selectedResult = optionsTerminatorResult == null ? match.result : {
|
|
1269
|
+
...match.result,
|
|
1270
|
+
next: {
|
|
1271
|
+
...match.result.next,
|
|
1272
|
+
buffer: optionsTerminatorResult.next.buffer,
|
|
1273
|
+
optionsTerminated: true
|
|
1274
|
+
},
|
|
1275
|
+
consumed: [...optionsTerminatorResult.consumed, ...match.result.consumed]
|
|
1276
|
+
};
|
|
1277
|
+
const parser = parsers[match.index];
|
|
1278
|
+
const mergedExec = mergeChildExec(context.exec, selectedResult.next.exec);
|
|
1279
|
+
return {
|
|
1280
|
+
success: true,
|
|
1281
|
+
next: {
|
|
1282
|
+
...context,
|
|
1283
|
+
buffer: selectedResult.next.buffer,
|
|
1284
|
+
optionsTerminated: selectedResult.next.optionsTerminated,
|
|
1285
|
+
state: createExclusiveState(context.state, match.index, parser, selectedResult),
|
|
1286
|
+
...mergedExec != null ? {
|
|
1287
|
+
exec: mergedExec,
|
|
1288
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1289
|
+
} : {}
|
|
1290
|
+
},
|
|
1291
|
+
consumed: selectedResult.consumed
|
|
1292
|
+
};
|
|
1293
|
+
};
|
|
1294
|
+
const resolveLongestMatchOptionsTerminator = (context, bestMatch, optionsTerminatorMatches) => {
|
|
1295
|
+
if (optionsTerminatorMatches.length < 1) return null;
|
|
1296
|
+
if (bestMatch != null && bestMatch.consumed > 0) return null;
|
|
1297
|
+
const activeState = normalizeExclusiveState(context.state);
|
|
1298
|
+
if (activeState != null && activeState[1].success && activeState[1].consumed.length > 0) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
|
|
1299
|
+
let selected = bestMatch == null ? null : {
|
|
1300
|
+
match: bestMatch,
|
|
1301
|
+
fromTerminator: false
|
|
1302
|
+
};
|
|
1303
|
+
for (const match of optionsTerminatorMatches) {
|
|
1304
|
+
if (!match.selectedAfterTerminator && (extractRequiredUsage(parsers[match.index].usage).length > 0 || isUnselectedBooleanOptionState(match.result.next.state))) continue;
|
|
1305
|
+
const selectedIsProvisional = selected != null && !!selected.match.result.provisional;
|
|
1306
|
+
const matchIsProvisional = !!match.result.provisional;
|
|
1307
|
+
if (selected == null || selectedIsProvisional && !matchIsProvisional || selectedIsProvisional === matchIsProvisional && match.index < selected.match.index) selected = {
|
|
1308
|
+
match,
|
|
1309
|
+
fromTerminator: true
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
if (selected != null) return commitLongestMatch(context, selected.match, selected.fromTerminator ? void 0 : optionsTerminatorMatches[0].result);
|
|
1313
|
+
return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
|
|
1314
|
+
};
|
|
1315
|
+
const parseSyncBranches = (context) => {
|
|
1225
1316
|
let bestMatch = null;
|
|
1317
|
+
const optionsTerminatorMatches = [];
|
|
1226
1318
|
let error = getInitialError(context);
|
|
1227
1319
|
const activeState = normalizeExclusiveState(context.state);
|
|
1228
1320
|
for (let i = 0; i < syncParsers.length; i++) {
|
|
1229
1321
|
const parser = syncParsers[i];
|
|
1230
|
-
const
|
|
1322
|
+
const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
|
|
1323
|
+
const result = parser.parse(childContext);
|
|
1324
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
1325
|
+
optionsTerminatorMatches.push({
|
|
1326
|
+
index: i,
|
|
1327
|
+
result,
|
|
1328
|
+
consumed: 1,
|
|
1329
|
+
...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
|
|
1330
|
+
});
|
|
1331
|
+
continue;
|
|
1332
|
+
}
|
|
1231
1333
|
if (result.success) {
|
|
1232
1334
|
const consumed = context.buffer.length - result.next.buffer.length;
|
|
1233
1335
|
const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
|
|
1234
1336
|
if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
|
|
1235
1337
|
index: i,
|
|
1236
|
-
parser,
|
|
1237
1338
|
result,
|
|
1238
1339
|
consumed
|
|
1239
1340
|
};
|
|
1240
1341
|
} else if (error.consumed < result.consumed) error = result;
|
|
1241
1342
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
success: true,
|
|
1246
|
-
next: {
|
|
1247
|
-
...context,
|
|
1248
|
-
buffer: bestMatch.result.next.buffer,
|
|
1249
|
-
optionsTerminated: bestMatch.result.next.optionsTerminated,
|
|
1250
|
-
state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
|
|
1251
|
-
...mergedExec != null ? {
|
|
1252
|
-
exec: mergedExec,
|
|
1253
|
-
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1254
|
-
} : {}
|
|
1255
|
-
},
|
|
1256
|
-
consumed: bestMatch.result.consumed
|
|
1257
|
-
};
|
|
1258
|
-
}
|
|
1343
|
+
const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
|
|
1344
|
+
if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
|
|
1345
|
+
if (bestMatch != null) return commitLongestMatch(context, bestMatch);
|
|
1259
1346
|
return {
|
|
1260
1347
|
...error,
|
|
1261
1348
|
success: false
|
|
1262
1349
|
};
|
|
1263
1350
|
};
|
|
1264
|
-
const
|
|
1351
|
+
const parseAsyncBranches = async (context) => {
|
|
1265
1352
|
let bestMatch = null;
|
|
1353
|
+
const optionsTerminatorMatches = [];
|
|
1266
1354
|
let error = getInitialError(context);
|
|
1267
1355
|
const activeState = normalizeExclusiveState(context.state);
|
|
1268
1356
|
for (let i = 0; i < parsers.length; i++) {
|
|
1269
1357
|
const parser = parsers[i];
|
|
1270
|
-
const
|
|
1358
|
+
const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
|
|
1359
|
+
const resultOrPromise = parser.parse(childContext);
|
|
1271
1360
|
const result = await resultOrPromise;
|
|
1361
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
1362
|
+
optionsTerminatorMatches.push({
|
|
1363
|
+
index: i,
|
|
1364
|
+
result,
|
|
1365
|
+
consumed: 1,
|
|
1366
|
+
...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
|
|
1367
|
+
});
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1272
1370
|
if (result.success) {
|
|
1273
1371
|
const consumed = context.buffer.length - result.next.buffer.length;
|
|
1274
1372
|
const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
|
|
1275
1373
|
if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
|
|
1276
1374
|
index: i,
|
|
1277
|
-
parser,
|
|
1278
1375
|
result,
|
|
1279
1376
|
consumed
|
|
1280
1377
|
};
|
|
1281
1378
|
} else if (error.consumed < result.consumed) error = result;
|
|
1282
1379
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
success: true,
|
|
1287
|
-
next: {
|
|
1288
|
-
...context,
|
|
1289
|
-
buffer: bestMatch.result.next.buffer,
|
|
1290
|
-
optionsTerminated: bestMatch.result.next.optionsTerminated,
|
|
1291
|
-
state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
|
|
1292
|
-
...mergedExec != null ? {
|
|
1293
|
-
exec: mergedExec,
|
|
1294
|
-
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1295
|
-
} : {}
|
|
1296
|
-
},
|
|
1297
|
-
consumed: bestMatch.result.consumed
|
|
1298
|
-
};
|
|
1299
|
-
}
|
|
1380
|
+
const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
|
|
1381
|
+
if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
|
|
1382
|
+
if (bestMatch != null) return commitLongestMatch(context, bestMatch);
|
|
1300
1383
|
return {
|
|
1301
1384
|
...error,
|
|
1302
1385
|
success: false
|
|
@@ -1319,7 +1402,7 @@ function longestMatch(...args) {
|
|
|
1319
1402
|
return extractExclusivePhase2Seed(parsers, state, exec, combinedMode);
|
|
1320
1403
|
},
|
|
1321
1404
|
parse(context) {
|
|
1322
|
-
return dispatchByMode(combinedMode, () =>
|
|
1405
|
+
return dispatchByMode(combinedMode, () => parseSyncBranches(context), () => parseAsyncBranches(context));
|
|
1323
1406
|
},
|
|
1324
1407
|
getSuggestRuntimeNodes(state, path) {
|
|
1325
1408
|
return getExclusiveSuggestRuntimeNodes(parsers, state, path);
|
|
@@ -1365,6 +1448,33 @@ function longestMatch(...args) {
|
|
|
1365
1448
|
return multiResult;
|
|
1366
1449
|
}
|
|
1367
1450
|
/**
|
|
1451
|
+
* Creates the initial parse error shared by object-like combinators.
|
|
1452
|
+
* @param context The current parser context.
|
|
1453
|
+
* @param noMatchContext The kinds of input accepted by the combinator.
|
|
1454
|
+
* @param errors Optional custom error formatters.
|
|
1455
|
+
* @returns A zero-consumption parse error.
|
|
1456
|
+
*/
|
|
1457
|
+
function createObjectLikeInitialError(context, noMatchContext, errors) {
|
|
1458
|
+
if (context.buffer.length < 1) {
|
|
1459
|
+
const customEndOfInput = errors?.endOfInput;
|
|
1460
|
+
return {
|
|
1461
|
+
consumed: 0,
|
|
1462
|
+
error: customEndOfInput ? typeof customEndOfInput === "function" ? customEndOfInput(noMatchContext) : customEndOfInput : generateNoMatchError(noMatchContext)
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
const token = context.buffer[0];
|
|
1466
|
+
const customMessage = errors?.unexpectedInput;
|
|
1467
|
+
if (customMessage) return {
|
|
1468
|
+
consumed: 0,
|
|
1469
|
+
error: typeof customMessage === "function" ? customMessage(token) : customMessage
|
|
1470
|
+
};
|
|
1471
|
+
const baseError = message`Unexpected option or argument: ${token}.`;
|
|
1472
|
+
return {
|
|
1473
|
+
consumed: 0,
|
|
1474
|
+
error: createErrorWithSuggestions(baseError, token, context.usage, "both", errors?.suggestions)
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1368
1478
|
* Internal sync helper for object suggest functionality.
|
|
1369
1479
|
* @internal
|
|
1370
1480
|
*/
|
|
@@ -1756,19 +1866,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
1756
1866
|
if (!options.allowDuplicates) checkDuplicateOptionNames(parserPairs.map(([field, parser]) => [field, parser.usage]));
|
|
1757
1867
|
const noMatchContext = analyzeNoMatchContext(parserKeys.map((k) => parsers[k]));
|
|
1758
1868
|
const combinedMode = parserKeys.some((k) => parsers[k].mode === "async") ? "async" : "sync";
|
|
1759
|
-
const getInitialError = (context) => (
|
|
1760
|
-
consumed: 0,
|
|
1761
|
-
error: context.buffer.length > 0 ? (() => {
|
|
1762
|
-
const token = context.buffer[0];
|
|
1763
|
-
const customMessage = options.errors?.unexpectedInput;
|
|
1764
|
-
if (customMessage) return typeof customMessage === "function" ? customMessage(token) : customMessage;
|
|
1765
|
-
const baseError = message`Unexpected option or argument: ${token}.`;
|
|
1766
|
-
return createErrorWithSuggestions(baseError, token, context.usage, "both", options.errors?.suggestions);
|
|
1767
|
-
})() : (() => {
|
|
1768
|
-
const customEndOfInput = options.errors?.endOfInput;
|
|
1769
|
-
return customEndOfInput ? typeof customEndOfInput === "function" ? customEndOfInput(noMatchContext) : customEndOfInput : generateNoMatchError(noMatchContext);
|
|
1770
|
-
})()
|
|
1771
|
-
});
|
|
1869
|
+
const getInitialError = (context) => createObjectLikeInitialError(context, noMatchContext, options.errors);
|
|
1772
1870
|
const parseSync = (context) => {
|
|
1773
1871
|
let error = getInitialError(context);
|
|
1774
1872
|
let currentContext = context;
|
|
@@ -2917,6 +3015,7 @@ function merge(...args) {
|
|
|
2917
3015
|
const syncSorted = syncWithIndex.toSorted(([a], [b]) => b.priority - a.priority);
|
|
2918
3016
|
const syncParsers = syncSorted.map(([p]) => p);
|
|
2919
3017
|
if (!options.allowDuplicates) checkDuplicateOptionNames(sorted.map(([parser, originalIndex]) => [String(originalIndex), parser.usage]));
|
|
3018
|
+
const noMatchContext = analyzeNoMatchContext(rawParsers);
|
|
2920
3019
|
const mergedFieldParsers = collectChildFieldParsers(parsers);
|
|
2921
3020
|
const duplicateOutputFieldNames = collectDuplicateFieldNames(mergedFieldParsers);
|
|
2922
3021
|
const parserStateKey = (index) => `__parser_${index}`;
|
|
@@ -3009,8 +3108,7 @@ function merge(...args) {
|
|
|
3009
3108
|
};
|
|
3010
3109
|
return {
|
|
3011
3110
|
success: false,
|
|
3012
|
-
|
|
3013
|
-
error: message`No matching option or argument found.`
|
|
3111
|
+
...createObjectLikeInitialError(context, noMatchContext)
|
|
3014
3112
|
};
|
|
3015
3113
|
};
|
|
3016
3114
|
const parseAsync = async (context) => {
|
|
@@ -3055,8 +3153,7 @@ function merge(...args) {
|
|
|
3055
3153
|
};
|
|
3056
3154
|
return {
|
|
3057
3155
|
success: false,
|
|
3058
|
-
|
|
3059
|
-
error: message`No matching option or argument found.`
|
|
3156
|
+
...createObjectLikeInitialError(context, noMatchContext)
|
|
3060
3157
|
};
|
|
3061
3158
|
};
|
|
3062
3159
|
const mergeParser = {
|