@optique/core 1.0.5 → 1.0.7
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 +353 -31
- package/dist/constructs.js +354 -32
- package/dist/internal/parser.cjs +34 -0
- package/dist/internal/parser.d.cts +52 -0
- package/dist/internal/parser.d.ts +52 -0
- package/dist/internal/parser.js +33 -1
- package/dist/modifiers.cjs +53 -0
- package/dist/modifiers.js +54 -1
- package/package.json +1 -1
package/dist/constructs.js
CHANGED
|
@@ -6,7 +6,7 @@ import { deduplicateDocFragments } from "./doc.js";
|
|
|
6
6
|
import { dispatchByMode, dispatchIterableByMode } from "./internal/mode-dispatch.js";
|
|
7
7
|
import { createDependencySourceState, dependencyId, isDependencySourceState, isPendingDependencySourceState, isWrappedDependencySource, wrappedDependencySourceMarker } from "./internal/dependency.js";
|
|
8
8
|
import { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, createDependencyRuntimeContext, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync } from "./dependency-runtime.js";
|
|
9
|
-
import { defineInheritedAnnotationParser, getParserSuggestRuntimeNodes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
|
|
9
|
+
import { defineInheritedAnnotationParser, defineParseLanes, getOwnParseLanes, getParserSuggestRuntimeNodes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
|
|
10
10
|
import { annotationViewTargets, getWrappedChildParseState, getWrappedChildState, reconcileObjectChildState, unwrapAnnotationView } from "./annotation-state.js";
|
|
11
11
|
import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
|
|
12
12
|
import { completeOrExtractPhase2Seed, extractPhase2Seed, extractPhase2SeedKey, phase2SeedFromValueResult } from "./phase2-seed.js";
|
|
@@ -77,8 +77,8 @@ function unionLeadingNames(parsers) {
|
|
|
77
77
|
* Computes `leadingNames` for shared-buffer compositions (`tuple()`,
|
|
78
78
|
* `object()`, `merge()`, `concat()`).
|
|
79
79
|
*
|
|
80
|
-
*
|
|
81
|
-
* round-robin parse loop). Once a
|
|
80
|
+
* Sources are processed in descending priority order (matching the
|
|
81
|
+
* round-robin parse loop). Once a source with `acceptingAnyToken` is
|
|
82
82
|
* encountered, no lower-priority children can match at position 0, so
|
|
83
83
|
* their names are excluded.
|
|
84
84
|
*/
|
|
@@ -1448,6 +1448,33 @@ function longestMatch(...args) {
|
|
|
1448
1448
|
return multiResult;
|
|
1449
1449
|
}
|
|
1450
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
|
+
/**
|
|
1451
1478
|
* Internal sync helper for object suggest functionality.
|
|
1452
1479
|
* @internal
|
|
1453
1480
|
*/
|
|
@@ -1839,19 +1866,53 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
1839
1866
|
if (!options.allowDuplicates) checkDuplicateOptionNames(parserPairs.map(([field, parser]) => [field, parser.usage]));
|
|
1840
1867
|
const noMatchContext = analyzeNoMatchContext(parserKeys.map((k) => parsers[k]));
|
|
1841
1868
|
const combinedMode = parserKeys.some((k) => parsers[k].mode === "async") ? "async" : "sync";
|
|
1842
|
-
const getInitialError = (context) => (
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1869
|
+
const getInitialError = (context) => createObjectLikeInitialError(context, noMatchContext, options.errors);
|
|
1870
|
+
const adaptFieldLaneResult = (context, field, parser, fieldState, result) => {
|
|
1871
|
+
if (!result.success) return result;
|
|
1872
|
+
if (result.consumed.length === 0 && result.next.state === fieldState) return {
|
|
1873
|
+
success: true,
|
|
1874
|
+
next: context,
|
|
1875
|
+
consumed: []
|
|
1876
|
+
};
|
|
1877
|
+
const mergedExec = mergeChildExec(context.exec, result.next.exec);
|
|
1878
|
+
const nextState = result.next.state === fieldState ? context.state : {
|
|
1879
|
+
...context.state,
|
|
1880
|
+
[field]: getWrappedChildState(context.state, result.next.state, parser)
|
|
1881
|
+
};
|
|
1882
|
+
return {
|
|
1883
|
+
success: true,
|
|
1884
|
+
next: {
|
|
1885
|
+
...context,
|
|
1886
|
+
buffer: result.next.buffer,
|
|
1887
|
+
optionsTerminated: result.next.optionsTerminated,
|
|
1888
|
+
state: nextState,
|
|
1889
|
+
...mergedExec != null ? {
|
|
1890
|
+
trace: mergedExec.trace,
|
|
1891
|
+
exec: mergedExec,
|
|
1892
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1893
|
+
} : {}
|
|
1894
|
+
},
|
|
1895
|
+
consumed: result.consumed
|
|
1896
|
+
};
|
|
1897
|
+
};
|
|
1898
|
+
const objectZeroConsumptionGroup = {};
|
|
1899
|
+
const objectParseLanes = parserPairs.map(([field, parser]) => ({
|
|
1900
|
+
priority: parser.priority,
|
|
1901
|
+
zeroConsumptionGroup: objectZeroConsumptionGroup,
|
|
1902
|
+
settlesZeroConsumption: false,
|
|
1903
|
+
leadingNames: parser.leadingNames,
|
|
1904
|
+
acceptingAnyToken: parser.acceptingAnyToken,
|
|
1905
|
+
parse(context) {
|
|
1906
|
+
const fieldState = createFieldStateGetter(context.state, getObjectParseChildState)(field, parser);
|
|
1907
|
+
return dispatchByMode(combinedMode, () => {
|
|
1908
|
+
const result = parser.parse(withChildContext$1(context, field, fieldState, parser));
|
|
1909
|
+
return adaptFieldLaneResult(context, field, parser, fieldState, result);
|
|
1910
|
+
}, async () => {
|
|
1911
|
+
const result = await parser.parse(withChildContext$1(context, field, fieldState, parser));
|
|
1912
|
+
return adaptFieldLaneResult(context, field, parser, fieldState, result);
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
}));
|
|
1855
1916
|
const parseSync = (context) => {
|
|
1856
1917
|
let error = getInitialError(context);
|
|
1857
1918
|
let currentContext = context;
|
|
@@ -2345,6 +2406,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
2345
2406
|
configurable: true,
|
|
2346
2407
|
enumerable: false
|
|
2347
2408
|
});
|
|
2409
|
+
defineParseLanes(objectParser, objectParseLanes);
|
|
2348
2410
|
defineInheritedAnnotationParser(objectParser);
|
|
2349
2411
|
return objectParser;
|
|
2350
2412
|
}
|
|
@@ -3000,6 +3062,7 @@ function merge(...args) {
|
|
|
3000
3062
|
const syncSorted = syncWithIndex.toSorted(([a], [b]) => b.priority - a.priority);
|
|
3001
3063
|
const syncParsers = syncSorted.map(([p]) => p);
|
|
3002
3064
|
if (!options.allowDuplicates) checkDuplicateOptionNames(sorted.map(([parser, originalIndex]) => [String(originalIndex), parser.usage]));
|
|
3065
|
+
const noMatchContext = analyzeNoMatchContext(rawParsers);
|
|
3003
3066
|
const mergedFieldParsers = collectChildFieldParsers(parsers);
|
|
3004
3067
|
const duplicateOutputFieldNames = collectDuplicateFieldNames(mergedFieldParsers);
|
|
3005
3068
|
const parserStateKey = (index) => `__parser_${index}`;
|
|
@@ -3011,17 +3074,17 @@ function merge(...args) {
|
|
|
3011
3074
|
if (parser.initialState === void 0) initialState[parserStateKey(i)] = void 0;
|
|
3012
3075
|
else if (parser.initialState && typeof parser.initialState === "object") for (const field in parser.initialState) initialState[field] = parser.initialState[field];
|
|
3013
3076
|
}
|
|
3014
|
-
const extractParserState = (parser,
|
|
3077
|
+
const extractParserState = (parser, state, index) => {
|
|
3015
3078
|
if (parser.initialState === void 0) {
|
|
3016
3079
|
const key = parserStateKey(index);
|
|
3017
|
-
if (
|
|
3080
|
+
if (state && typeof state === "object" && key in state) return state[key];
|
|
3018
3081
|
return void 0;
|
|
3019
3082
|
} else if (parser.initialState && typeof parser.initialState === "object") {
|
|
3020
3083
|
const localStateKey = localObjectStateKey(index);
|
|
3021
|
-
if (shouldPreserveLocalChildState(parser) &&
|
|
3022
|
-
if (
|
|
3084
|
+
if (shouldPreserveLocalChildState(parser) && state && typeof state === "object" && localStateKey in state) return state[localStateKey];
|
|
3085
|
+
if (state && typeof state === "object") {
|
|
3023
3086
|
const extractedState = {};
|
|
3024
|
-
for (const field in parser.initialState) extractedState[field] = field in
|
|
3087
|
+
for (const field in parser.initialState) extractedState[field] = field in state ? state[field] : parser.initialState[field];
|
|
3025
3088
|
return extractedState;
|
|
3026
3089
|
}
|
|
3027
3090
|
return parser.initialState;
|
|
@@ -3051,12 +3114,145 @@ function merge(...args) {
|
|
|
3051
3114
|
[localObjectStateKey(index)]: result.next.state
|
|
3052
3115
|
};
|
|
3053
3116
|
};
|
|
3054
|
-
const
|
|
3117
|
+
const adaptMergeLaneResult = (parser, context, parserState, parsedState, result, index) => {
|
|
3118
|
+
if (!result.success) return result;
|
|
3119
|
+
const mergedExec = mergeChildExec(context.exec, result.next.exec);
|
|
3120
|
+
const newState = result.next.state === parsedState ? context.state : mergeResultState(parser, context, parserState, result, index);
|
|
3121
|
+
return {
|
|
3122
|
+
success: true,
|
|
3123
|
+
next: {
|
|
3124
|
+
...context,
|
|
3125
|
+
buffer: result.next.buffer,
|
|
3126
|
+
optionsTerminated: result.next.optionsTerminated,
|
|
3127
|
+
state: newState,
|
|
3128
|
+
...mergedExec != null ? {
|
|
3129
|
+
trace: mergedExec.trace,
|
|
3130
|
+
exec: mergedExec,
|
|
3131
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
3132
|
+
} : {}
|
|
3133
|
+
},
|
|
3134
|
+
consumed: result.consumed
|
|
3135
|
+
};
|
|
3136
|
+
};
|
|
3137
|
+
const childrenInDeclarationOrder = sorted.map(([parser, originalIndex], sortedIndex) => ({
|
|
3138
|
+
parser,
|
|
3139
|
+
originalIndex,
|
|
3140
|
+
sortedIndex
|
|
3141
|
+
})).toSorted((a, b) => a.originalIndex - b.originalIndex);
|
|
3142
|
+
const mergeParseLanes = childrenInDeclarationOrder.flatMap(({ parser, sortedIndex }) => {
|
|
3143
|
+
const occurrenceConsumptionGroups = /* @__PURE__ */ new WeakMap();
|
|
3144
|
+
const occurrenceZeroConsumptionGroups = /* @__PURE__ */ new WeakMap();
|
|
3145
|
+
const scopeConsumptionGroup = (group$1) => {
|
|
3146
|
+
const existing = occurrenceConsumptionGroups.get(group$1);
|
|
3147
|
+
if (existing != null) return existing;
|
|
3148
|
+
const scoped = {};
|
|
3149
|
+
occurrenceConsumptionGroups.set(group$1, scoped);
|
|
3150
|
+
return scoped;
|
|
3151
|
+
};
|
|
3152
|
+
const childLanes = getOwnParseLanes(parser);
|
|
3153
|
+
const lanes = childLanes ?? [{
|
|
3154
|
+
priority: parser.priority,
|
|
3155
|
+
leadingNames: parser.leadingNames,
|
|
3156
|
+
acceptingAnyToken: parser.acceptingAnyToken,
|
|
3157
|
+
parse(context) {
|
|
3158
|
+
return parser.parse(context);
|
|
3159
|
+
}
|
|
3160
|
+
}];
|
|
3161
|
+
return lanes.map((lane) => ({
|
|
3162
|
+
priority: lane.priority,
|
|
3163
|
+
zeroConsumptionGroup: (() => {
|
|
3164
|
+
const group$1 = lane.zeroConsumptionGroup ?? lane;
|
|
3165
|
+
const existing = occurrenceZeroConsumptionGroups.get(group$1);
|
|
3166
|
+
if (existing != null) return existing;
|
|
3167
|
+
const scoped = {};
|
|
3168
|
+
occurrenceZeroConsumptionGroups.set(group$1, scoped);
|
|
3169
|
+
return scoped;
|
|
3170
|
+
})(),
|
|
3171
|
+
settlesZeroConsumption: lane.settlesZeroConsumption,
|
|
3172
|
+
leadingNames: lane.leadingNames,
|
|
3173
|
+
acceptingAnyToken: lane.acceptingAnyToken,
|
|
3174
|
+
requiredConsumptionGroups: lane.requiredConsumptionGroups?.map((group$1) => ({
|
|
3175
|
+
id: scopeConsumptionGroup(group$1.id),
|
|
3176
|
+
...group$1.isActive == null ? {} : { isActive(state) {
|
|
3177
|
+
if (state == null || typeof state !== "object") return false;
|
|
3178
|
+
const parserState = extractParserState(parser, state, sortedIndex);
|
|
3179
|
+
return group$1.isActive?.(parserState) ?? true;
|
|
3180
|
+
} }
|
|
3181
|
+
})),
|
|
3182
|
+
parse(context) {
|
|
3183
|
+
const parserState = extractParserState(parser, context.state, sortedIndex);
|
|
3184
|
+
const childContext = withChildContext$1(context, sortedIndex, parserState, parser);
|
|
3185
|
+
return dispatchByMode(combinedMode, () => {
|
|
3186
|
+
const result = lane.parse(childContext);
|
|
3187
|
+
return adaptMergeLaneResult(parser, context, parserState, childContext.state, result, sortedIndex);
|
|
3188
|
+
}, async () => {
|
|
3189
|
+
const result = await lane.parse(childContext);
|
|
3190
|
+
return adaptMergeLaneResult(parser, context, parserState, childContext.state, result, sortedIndex);
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
3193
|
+
}));
|
|
3194
|
+
}).toSorted((a, b) => b.priority - a.priority);
|
|
3195
|
+
const settleZeroConsumptionLanes = (context, laneResults) => {
|
|
3196
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3197
|
+
for (const lane of mergeParseLanes) {
|
|
3198
|
+
const group$1 = lane.zeroConsumptionGroup ?? lane;
|
|
3199
|
+
const groupedLanes = groups.get(group$1);
|
|
3200
|
+
if (groupedLanes == null) groups.set(group$1, [lane]);
|
|
3201
|
+
else groupedLanes.push(lane);
|
|
3202
|
+
}
|
|
3203
|
+
const settledGroups = [];
|
|
3204
|
+
for (const groupedLanes of groups.values()) {
|
|
3205
|
+
const groupSucceeded = groupedLanes.every((lane) => {
|
|
3206
|
+
const result = laneResults.get(lane);
|
|
3207
|
+
return lane.settlesZeroConsumption !== false && result?.success === true && result.consumed.length === 0 && (lane.requiredConsumptionGroups?.length ?? 0) === 0;
|
|
3208
|
+
});
|
|
3209
|
+
if (!groupSucceeded) return null;
|
|
3210
|
+
settledGroups.push(groupedLanes);
|
|
3211
|
+
}
|
|
3212
|
+
let settledContext = context;
|
|
3213
|
+
for (const groupedLanes of settledGroups) for (const lane of groupedLanes) {
|
|
3214
|
+
const result = laneResults.get(lane);
|
|
3215
|
+
if (result?.success !== true) continue;
|
|
3216
|
+
const originalState = context.state;
|
|
3217
|
+
const resultState = result.next.state;
|
|
3218
|
+
const nextState = { ...settledContext.state };
|
|
3219
|
+
const stateKeys = new Set([...Reflect.ownKeys(originalState), ...Reflect.ownKeys(resultState)]);
|
|
3220
|
+
for (const key of stateKeys) {
|
|
3221
|
+
const originalHasKey = Object.hasOwn(originalState, key);
|
|
3222
|
+
const resultHasKey = Object.hasOwn(resultState, key);
|
|
3223
|
+
if (originalHasKey === resultHasKey && originalState[key] === resultState[key]) continue;
|
|
3224
|
+
if (resultHasKey) nextState[key] = resultState[key];
|
|
3225
|
+
else delete nextState[key];
|
|
3226
|
+
}
|
|
3227
|
+
const mergedExec = mergeChildExec(settledContext.exec, result.next.exec);
|
|
3228
|
+
settledContext = {
|
|
3229
|
+
...settledContext,
|
|
3230
|
+
buffer: result.next.buffer,
|
|
3231
|
+
optionsTerminated: result.next.optionsTerminated,
|
|
3232
|
+
state: nextState,
|
|
3233
|
+
...mergedExec != null ? {
|
|
3234
|
+
trace: mergedExec.trace,
|
|
3235
|
+
exec: mergedExec,
|
|
3236
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
3237
|
+
} : {}
|
|
3238
|
+
};
|
|
3239
|
+
}
|
|
3240
|
+
return {
|
|
3241
|
+
success: true,
|
|
3242
|
+
next: settledContext,
|
|
3243
|
+
consumed: []
|
|
3244
|
+
};
|
|
3245
|
+
};
|
|
3246
|
+
const canTryPositionalLaneAfterFailure = (lane, context) => {
|
|
3247
|
+
const token = context.buffer[0];
|
|
3248
|
+
return token != null && (context.optionsTerminated || !token.startsWith("-")) && (lane.acceptingAnyToken || lane.leadingNames.has(token));
|
|
3249
|
+
};
|
|
3250
|
+
const parseChildrenSync = (context) => {
|
|
3055
3251
|
let currentContext = context;
|
|
3056
3252
|
let zeroConsumedSuccess = null;
|
|
3057
3253
|
for (let i = 0; i < syncParsers.length; i++) {
|
|
3058
3254
|
const parser = syncParsers[i];
|
|
3059
|
-
const parserState = extractParserState(parser, currentContext, i);
|
|
3255
|
+
const parserState = extractParserState(parser, currentContext.state, i);
|
|
3060
3256
|
const result = parser.parse(withChildContext$1(currentContext, i, parserState, parser));
|
|
3061
3257
|
if (result.success) {
|
|
3062
3258
|
const mergedExec = mergeChildExec(currentContext.exec, result.next.exec);
|
|
@@ -3092,16 +3288,15 @@ function merge(...args) {
|
|
|
3092
3288
|
};
|
|
3093
3289
|
return {
|
|
3094
3290
|
success: false,
|
|
3095
|
-
|
|
3096
|
-
error: message`No matching option or argument found.`
|
|
3291
|
+
...createObjectLikeInitialError(context, noMatchContext)
|
|
3097
3292
|
};
|
|
3098
3293
|
};
|
|
3099
|
-
const
|
|
3294
|
+
const parseChildrenAsync = async (context) => {
|
|
3100
3295
|
let currentContext = context;
|
|
3101
3296
|
let zeroConsumedSuccess = null;
|
|
3102
3297
|
for (let i = 0; i < parsers.length; i++) {
|
|
3103
3298
|
const parser = parsers[i];
|
|
3104
|
-
const parserState = extractParserState(parser, currentContext, i);
|
|
3299
|
+
const parserState = extractParserState(parser, currentContext.state, i);
|
|
3105
3300
|
const resultOrPromise = parser.parse(withChildContext$1(currentContext, i, parserState, parser));
|
|
3106
3301
|
const result = await resultOrPromise;
|
|
3107
3302
|
if (result.success) {
|
|
@@ -3138,8 +3333,133 @@ function merge(...args) {
|
|
|
3138
3333
|
};
|
|
3139
3334
|
return {
|
|
3140
3335
|
success: false,
|
|
3141
|
-
|
|
3142
|
-
|
|
3336
|
+
...createObjectLikeInitialError(context, noMatchContext)
|
|
3337
|
+
};
|
|
3338
|
+
};
|
|
3339
|
+
const parseSync = (context) => {
|
|
3340
|
+
let currentContext = context;
|
|
3341
|
+
let error = createObjectLikeInitialError(context, noMatchContext);
|
|
3342
|
+
const allConsumed = [];
|
|
3343
|
+
const consumedLanes = /* @__PURE__ */ new Set();
|
|
3344
|
+
const consumedGroups = /* @__PURE__ */ new Set();
|
|
3345
|
+
const laneResults = /* @__PURE__ */ new Map();
|
|
3346
|
+
let attemptedLane = false;
|
|
3347
|
+
let madeProgress = true;
|
|
3348
|
+
while (madeProgress && currentContext.buffer.length > 0) {
|
|
3349
|
+
madeProgress = false;
|
|
3350
|
+
let consumingError = null;
|
|
3351
|
+
for (const lane of mergeParseLanes) {
|
|
3352
|
+
if (consumingError != null && lane.priority < consumingError.priority) {
|
|
3353
|
+
if (!canTryPositionalLaneAfterFailure(lane, currentContext)) continue;
|
|
3354
|
+
}
|
|
3355
|
+
attemptedLane = true;
|
|
3356
|
+
const result = lane.parse(currentContext);
|
|
3357
|
+
laneResults.set(lane, result);
|
|
3358
|
+
if (result.success && result.consumed.length > 0) {
|
|
3359
|
+
currentContext = result.next;
|
|
3360
|
+
allConsumed.push(...result.consumed);
|
|
3361
|
+
consumedLanes.add(lane);
|
|
3362
|
+
for (const group$1 of lane.requiredConsumptionGroups ?? []) if (group$1.isActive?.(result.next.state) !== false) consumedGroups.add(group$1.id);
|
|
3363
|
+
madeProgress = true;
|
|
3364
|
+
break;
|
|
3365
|
+
}
|
|
3366
|
+
if (!result.success) {
|
|
3367
|
+
if (result.consumed > 0 && consumingError == null) consumingError = {
|
|
3368
|
+
priority: lane.priority,
|
|
3369
|
+
result
|
|
3370
|
+
};
|
|
3371
|
+
if (error.consumed < result.consumed) error = result;
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
if (!madeProgress && consumingError != null && allConsumed.length === 0) return consumingError.result;
|
|
3375
|
+
}
|
|
3376
|
+
if (allConsumed.length === 0) {
|
|
3377
|
+
if (attemptedLane) {
|
|
3378
|
+
const settled = settleZeroConsumptionLanes(context, laneResults);
|
|
3379
|
+
return settled ?? {
|
|
3380
|
+
...error,
|
|
3381
|
+
success: false
|
|
3382
|
+
};
|
|
3383
|
+
}
|
|
3384
|
+
const fallback = parseChildrenSync(context);
|
|
3385
|
+
if (!fallback.success && fallback.consumed < error.consumed) return {
|
|
3386
|
+
...error,
|
|
3387
|
+
success: false
|
|
3388
|
+
};
|
|
3389
|
+
return fallback;
|
|
3390
|
+
}
|
|
3391
|
+
for (const lane of mergeParseLanes) {
|
|
3392
|
+
if (consumedLanes.has(lane) || lane.leadingNames.size > 0 || lane.acceptingAnyToken || lane.requiredConsumptionGroups?.some((group$1) => !consumedGroups.has(group$1.id)) === true) continue;
|
|
3393
|
+
const result = lane.parse(currentContext);
|
|
3394
|
+
if (result.success && result.consumed.length === 0 && result.next.state !== currentContext.state) currentContext = result.next;
|
|
3395
|
+
}
|
|
3396
|
+
return {
|
|
3397
|
+
success: true,
|
|
3398
|
+
next: currentContext,
|
|
3399
|
+
consumed: allConsumed
|
|
3400
|
+
};
|
|
3401
|
+
};
|
|
3402
|
+
const parseAsync = async (context) => {
|
|
3403
|
+
let currentContext = context;
|
|
3404
|
+
let error = createObjectLikeInitialError(context, noMatchContext);
|
|
3405
|
+
const allConsumed = [];
|
|
3406
|
+
const consumedLanes = /* @__PURE__ */ new Set();
|
|
3407
|
+
const consumedGroups = /* @__PURE__ */ new Set();
|
|
3408
|
+
const laneResults = /* @__PURE__ */ new Map();
|
|
3409
|
+
let attemptedLane = false;
|
|
3410
|
+
let madeProgress = true;
|
|
3411
|
+
while (madeProgress && currentContext.buffer.length > 0) {
|
|
3412
|
+
madeProgress = false;
|
|
3413
|
+
let consumingError = null;
|
|
3414
|
+
for (const lane of mergeParseLanes) {
|
|
3415
|
+
if (consumingError != null && lane.priority < consumingError.priority) {
|
|
3416
|
+
if (!canTryPositionalLaneAfterFailure(lane, currentContext)) continue;
|
|
3417
|
+
}
|
|
3418
|
+
attemptedLane = true;
|
|
3419
|
+
const result = await lane.parse(currentContext);
|
|
3420
|
+
laneResults.set(lane, result);
|
|
3421
|
+
if (result.success && result.consumed.length > 0) {
|
|
3422
|
+
currentContext = result.next;
|
|
3423
|
+
allConsumed.push(...result.consumed);
|
|
3424
|
+
consumedLanes.add(lane);
|
|
3425
|
+
for (const group$1 of lane.requiredConsumptionGroups ?? []) if (group$1.isActive?.(result.next.state) !== false) consumedGroups.add(group$1.id);
|
|
3426
|
+
madeProgress = true;
|
|
3427
|
+
break;
|
|
3428
|
+
}
|
|
3429
|
+
if (!result.success) {
|
|
3430
|
+
if (result.consumed > 0 && consumingError == null) consumingError = {
|
|
3431
|
+
priority: lane.priority,
|
|
3432
|
+
result
|
|
3433
|
+
};
|
|
3434
|
+
if (error.consumed < result.consumed) error = result;
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
if (!madeProgress && consumingError != null && allConsumed.length === 0) return consumingError.result;
|
|
3438
|
+
}
|
|
3439
|
+
if (allConsumed.length === 0) {
|
|
3440
|
+
if (attemptedLane) {
|
|
3441
|
+
const settled = settleZeroConsumptionLanes(context, laneResults);
|
|
3442
|
+
return settled ?? {
|
|
3443
|
+
...error,
|
|
3444
|
+
success: false
|
|
3445
|
+
};
|
|
3446
|
+
}
|
|
3447
|
+
const fallback = await parseChildrenAsync(context);
|
|
3448
|
+
if (!fallback.success && fallback.consumed < error.consumed) return {
|
|
3449
|
+
...error,
|
|
3450
|
+
success: false
|
|
3451
|
+
};
|
|
3452
|
+
return fallback;
|
|
3453
|
+
}
|
|
3454
|
+
for (const lane of mergeParseLanes) {
|
|
3455
|
+
if (consumedLanes.has(lane) || lane.leadingNames.size > 0 || lane.acceptingAnyToken || lane.requiredConsumptionGroups?.some((group$1) => !consumedGroups.has(group$1.id)) === true) continue;
|
|
3456
|
+
const result = await lane.parse(currentContext);
|
|
3457
|
+
if (result.success && result.consumed.length === 0 && result.next.state !== currentContext.state) currentContext = result.next;
|
|
3458
|
+
}
|
|
3459
|
+
return {
|
|
3460
|
+
success: true,
|
|
3461
|
+
next: currentContext,
|
|
3462
|
+
consumed: allConsumed
|
|
3143
3463
|
};
|
|
3144
3464
|
};
|
|
3145
3465
|
const mergeParser = {
|
|
@@ -3147,10 +3467,10 @@ function merge(...args) {
|
|
|
3147
3467
|
$valueType: [],
|
|
3148
3468
|
$stateType: [],
|
|
3149
3469
|
[fieldParsersKey]: mergedFieldParsers,
|
|
3150
|
-
priority: Math.max(...
|
|
3470
|
+
priority: Math.max(...mergeParseLanes.map((lane) => lane.priority)),
|
|
3151
3471
|
usage: applyHiddenToUsage(parsers.flatMap((p) => p.usage), options.hidden),
|
|
3152
|
-
leadingNames: sharedBufferLeadingNames(
|
|
3153
|
-
acceptingAnyToken:
|
|
3472
|
+
leadingNames: sharedBufferLeadingNames(mergeParseLanes),
|
|
3473
|
+
acceptingAnyToken: mergeParseLanes.some((lane) => lane.acceptingAnyToken),
|
|
3154
3474
|
initialState,
|
|
3155
3475
|
parse(context) {
|
|
3156
3476
|
if (isAsync) return parseAsync(context);
|
|
@@ -3619,6 +3939,7 @@ function merge(...args) {
|
|
|
3619
3939
|
};
|
|
3620
3940
|
}
|
|
3621
3941
|
};
|
|
3942
|
+
defineParseLanes(mergeParser, mergeParseLanes);
|
|
3622
3943
|
defineInheritedAnnotationParser(mergeParser);
|
|
3623
3944
|
return mergeParser;
|
|
3624
3945
|
}
|
|
@@ -4296,6 +4617,7 @@ function group(label, parser, options = {}) {
|
|
|
4296
4617
|
};
|
|
4297
4618
|
}
|
|
4298
4619
|
};
|
|
4620
|
+
defineParseLanes(groupParser, getOwnParseLanes(parser));
|
|
4299
4621
|
Object.defineProperty(groupParser, extractPhase2SeedKey, {
|
|
4300
4622
|
value(state, exec) {
|
|
4301
4623
|
return extractPhase2Seed(parser, state, exec);
|
package/dist/internal/parser.cjs
CHANGED
|
@@ -8,6 +8,12 @@ const require_input_trace = require('../input-trace.cjs');
|
|
|
8
8
|
|
|
9
9
|
//#region src/internal/parser.ts
|
|
10
10
|
/**
|
|
11
|
+
* Internal symbol used by transparent combinators to expose independently
|
|
12
|
+
* competing parse operations to shared-buffer parents.
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
const parseLanesKey = Symbol("parseLanes");
|
|
16
|
+
/**
|
|
11
17
|
* Internal marker for wrappers whose `{ hasCliValue: false }` states should
|
|
12
18
|
* be treated as unmatched dependency-source states during completion-time
|
|
13
19
|
* Phase 1.
|
|
@@ -385,6 +391,32 @@ function composeWrappedSourceMetadata(dependencyMetadata, wrapSource) {
|
|
|
385
391
|
};
|
|
386
392
|
}
|
|
387
393
|
/**
|
|
394
|
+
* Defines internal parse-lane metadata without exposing it through object
|
|
395
|
+
* spreads used by custom parser wrappers.
|
|
396
|
+
*
|
|
397
|
+
* @internal
|
|
398
|
+
*/
|
|
399
|
+
function defineParseLanes(parser, lanes) {
|
|
400
|
+
if (lanes == null) return;
|
|
401
|
+
Object.defineProperty(parser, parseLanesKey, {
|
|
402
|
+
value: lanes,
|
|
403
|
+
configurable: true,
|
|
404
|
+
enumerable: false
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Gets parse-lane metadata defined directly on a parser.
|
|
409
|
+
*
|
|
410
|
+
* Inherited metadata belongs to the parser's prototype and must not bypass an
|
|
411
|
+
* overriding `parse()` implementation on a custom wrapper.
|
|
412
|
+
*
|
|
413
|
+
* @internal
|
|
414
|
+
*/
|
|
415
|
+
function getOwnParseLanes(parser) {
|
|
416
|
+
if (!Object.hasOwn(parser, parseLanesKey)) return void 0;
|
|
417
|
+
return parser[parseLanesKey];
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
388
420
|
* Marks a parser as inheriting parent-state annotations through wrapper-state
|
|
389
421
|
* reconstruction.
|
|
390
422
|
*
|
|
@@ -712,11 +744,13 @@ exports.annotationWrapperRequiresSourceBindingKey = annotationWrapperRequiresSou
|
|
|
712
744
|
exports.composeWrappedSourceMetadata = composeWrappedSourceMetadata;
|
|
713
745
|
exports.createParserContext = createParserContext;
|
|
714
746
|
exports.defineInheritedAnnotationParser = defineInheritedAnnotationParser;
|
|
747
|
+
exports.defineParseLanes = defineParseLanes;
|
|
715
748
|
exports.defineSourceBindingOnlyAnnotationCompletionParser = defineSourceBindingOnlyAnnotationCompletionParser;
|
|
716
749
|
exports.getDelegatingSuggestRuntimeNodes = getDelegatingSuggestRuntimeNodes;
|
|
717
750
|
exports.getDocPage = getDocPage;
|
|
718
751
|
exports.getDocPageAsync = getDocPageAsync;
|
|
719
752
|
exports.getDocPageSync = getDocPageSync;
|
|
753
|
+
exports.getOwnParseLanes = getOwnParseLanes;
|
|
720
754
|
exports.getParserSuggestRuntimeNodes = getParserSuggestRuntimeNodes;
|
|
721
755
|
exports.inheritParentAnnotationsKey = inheritParentAnnotationsKey;
|
|
722
756
|
exports.parse = parse;
|
|
@@ -42,6 +42,51 @@ type ModeValue<M extends Mode, T> = M extends "async" ? Promise<T> : T;
|
|
|
42
42
|
* @since 0.9.0
|
|
43
43
|
*/
|
|
44
44
|
type ModeIterable<M extends Mode, T> = M extends "async" ? AsyncIterable<T> : Iterable<T>;
|
|
45
|
+
/**
|
|
46
|
+
* Internal symbol used by transparent combinators to expose independently
|
|
47
|
+
* competing parse operations to shared-buffer parents.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
declare const parseLanesKey: unique symbol;
|
|
51
|
+
/**
|
|
52
|
+
* An owner-consumption requirement for a parse lane.
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
interface ParseLaneConsumptionGroup {
|
|
56
|
+
/** Identity shared by lanes belonging to the same owning parser. */
|
|
57
|
+
readonly id: object;
|
|
58
|
+
/** Whether the owning parser is active after a consuming lane succeeds. */
|
|
59
|
+
readonly isActive?: (state: unknown) => boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A state-preserving parse operation that competes at one exact priority.
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
interface ParseLane<TState> {
|
|
66
|
+
/** The exact priority at which this lane competes. */
|
|
67
|
+
readonly priority: number;
|
|
68
|
+
/**
|
|
69
|
+
* Identity shared by lanes that must all succeed before their
|
|
70
|
+
* zero-consumption state updates can be committed.
|
|
71
|
+
*/
|
|
72
|
+
readonly zeroConsumptionGroup?: object;
|
|
73
|
+
/**
|
|
74
|
+
* Whether a zero-consumption success can settle the owning parser while
|
|
75
|
+
* input remains. Defaults to `true`.
|
|
76
|
+
*/
|
|
77
|
+
readonly settlesZeroConsumption?: boolean;
|
|
78
|
+
/** Fixed tokens reachable through this lane. */
|
|
79
|
+
readonly leadingNames: ReadonlySet<string>;
|
|
80
|
+
/** Whether this lane accepts any positional token. */
|
|
81
|
+
readonly acceptingAnyToken: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Groups in which a zero-consumption update is valid only after another
|
|
84
|
+
* lane in every group has consumed input during the same arbitration.
|
|
85
|
+
*/
|
|
86
|
+
readonly requiredConsumptionGroups?: readonly ParseLaneConsumptionGroup[];
|
|
87
|
+
/** Parses through the owning parser's state and execution context. */
|
|
88
|
+
parse(context: ParserContext<TState>): ParserResult<TState> | Promise<ParserResult<TState>>;
|
|
89
|
+
}
|
|
45
90
|
/**
|
|
46
91
|
* Combines multiple modes into a single mode.
|
|
47
92
|
* If any mode is `"async"`, the result is `"async"`; otherwise `"sync"`.
|
|
@@ -148,6 +193,13 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
|
|
|
148
193
|
* state when parsing starts.
|
|
149
194
|
*/
|
|
150
195
|
readonly initialState: TState;
|
|
196
|
+
/**
|
|
197
|
+
* Independently competing parse operations exposed by transparent
|
|
198
|
+
* combinators. Shared-buffer parents use these to arbitrate below an
|
|
199
|
+
* aggregate parser boundary without bypassing the owner's state adapters.
|
|
200
|
+
* @internal
|
|
201
|
+
*/
|
|
202
|
+
readonly [parseLanesKey]?: readonly ParseLane<TState>[];
|
|
151
203
|
/**
|
|
152
204
|
* Internal marker for wrappers whose `{ hasCliValue: false }` states should
|
|
153
205
|
* be treated as unmatched dependency-source states during completion-time
|
|
@@ -42,6 +42,51 @@ type ModeValue<M extends Mode, T> = M extends "async" ? Promise<T> : T;
|
|
|
42
42
|
* @since 0.9.0
|
|
43
43
|
*/
|
|
44
44
|
type ModeIterable<M extends Mode, T> = M extends "async" ? AsyncIterable<T> : Iterable<T>;
|
|
45
|
+
/**
|
|
46
|
+
* Internal symbol used by transparent combinators to expose independently
|
|
47
|
+
* competing parse operations to shared-buffer parents.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
declare const parseLanesKey: unique symbol;
|
|
51
|
+
/**
|
|
52
|
+
* An owner-consumption requirement for a parse lane.
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
interface ParseLaneConsumptionGroup {
|
|
56
|
+
/** Identity shared by lanes belonging to the same owning parser. */
|
|
57
|
+
readonly id: object;
|
|
58
|
+
/** Whether the owning parser is active after a consuming lane succeeds. */
|
|
59
|
+
readonly isActive?: (state: unknown) => boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A state-preserving parse operation that competes at one exact priority.
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
interface ParseLane<TState> {
|
|
66
|
+
/** The exact priority at which this lane competes. */
|
|
67
|
+
readonly priority: number;
|
|
68
|
+
/**
|
|
69
|
+
* Identity shared by lanes that must all succeed before their
|
|
70
|
+
* zero-consumption state updates can be committed.
|
|
71
|
+
*/
|
|
72
|
+
readonly zeroConsumptionGroup?: object;
|
|
73
|
+
/**
|
|
74
|
+
* Whether a zero-consumption success can settle the owning parser while
|
|
75
|
+
* input remains. Defaults to `true`.
|
|
76
|
+
*/
|
|
77
|
+
readonly settlesZeroConsumption?: boolean;
|
|
78
|
+
/** Fixed tokens reachable through this lane. */
|
|
79
|
+
readonly leadingNames: ReadonlySet<string>;
|
|
80
|
+
/** Whether this lane accepts any positional token. */
|
|
81
|
+
readonly acceptingAnyToken: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Groups in which a zero-consumption update is valid only after another
|
|
84
|
+
* lane in every group has consumed input during the same arbitration.
|
|
85
|
+
*/
|
|
86
|
+
readonly requiredConsumptionGroups?: readonly ParseLaneConsumptionGroup[];
|
|
87
|
+
/** Parses through the owning parser's state and execution context. */
|
|
88
|
+
parse(context: ParserContext<TState>): ParserResult<TState> | Promise<ParserResult<TState>>;
|
|
89
|
+
}
|
|
45
90
|
/**
|
|
46
91
|
* Combines multiple modes into a single mode.
|
|
47
92
|
* If any mode is `"async"`, the result is `"async"`; otherwise `"sync"`.
|
|
@@ -148,6 +193,13 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
|
|
|
148
193
|
* state when parsing starts.
|
|
149
194
|
*/
|
|
150
195
|
readonly initialState: TState;
|
|
196
|
+
/**
|
|
197
|
+
* Independently competing parse operations exposed by transparent
|
|
198
|
+
* combinators. Shared-buffer parents use these to arbitrate below an
|
|
199
|
+
* aggregate parser boundary without bypassing the owner's state adapters.
|
|
200
|
+
* @internal
|
|
201
|
+
*/
|
|
202
|
+
readonly [parseLanesKey]?: readonly ParseLane<TState>[];
|
|
151
203
|
/**
|
|
152
204
|
* Internal marker for wrappers whose `{ hasCliValue: false }` states should
|
|
153
205
|
* be treated as unmatched dependency-source states during completion-time
|