@optique/core 1.4.0-dev.2708 → 1.4.0-dev.2711
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 +43 -16
- package/dist/constructs.js +43 -16
- package/dist/internal/empty-input.cjs +368 -0
- package/dist/internal/empty-input.js +355 -0
- package/dist/internal/usage.cjs +248 -0
- package/dist/internal/usage.d.cts +42 -0
- package/dist/internal/usage.d.ts +42 -0
- package/dist/internal/usage.js +246 -0
- package/dist/modifiers.cjs +49 -6
- package/dist/modifiers.js +49 -6
- package/dist/primitives.cjs +19 -4
- package/dist/primitives.js +19 -4
- package/dist/usage.cjs +47 -13
- package/dist/usage.d.cts +20 -0
- package/dist/usage.d.ts +20 -0
- package/dist/usage.js +47 -13
- package/package.json +10 -2
package/dist/constructs.cjs
CHANGED
|
@@ -8,6 +8,7 @@ const require_internal_dependency = require('./internal/dependency.cjs');
|
|
|
8
8
|
const require_dependency_runtime = require('./dependency-runtime.cjs');
|
|
9
9
|
const require_internal_parser = require('./internal/parser.cjs');
|
|
10
10
|
const require_annotation_state = require('./annotation-state.cjs');
|
|
11
|
+
const require_empty_input = require('./internal/empty-input.cjs');
|
|
11
12
|
const require_execution_context = require('./execution-context.cjs');
|
|
12
13
|
const require_usage_internals = require('./usage-internals.cjs');
|
|
13
14
|
const require_short_option = require('./short-option.cjs');
|
|
@@ -890,7 +891,8 @@ function applyHiddenToUsageTerm(term, hidden) {
|
|
|
890
891
|
if (term.type === "multiple") return {
|
|
891
892
|
type: "multiple",
|
|
892
893
|
terms: applyHiddenToUsage(term.terms, hidden),
|
|
893
|
-
min: term.min
|
|
894
|
+
min: term.min,
|
|
895
|
+
...term.acceptsEmpty == null ? {} : { acceptsEmpty: term.acceptsEmpty }
|
|
894
896
|
};
|
|
895
897
|
if (term.type === "sequence") return {
|
|
896
898
|
type: "sequence",
|
|
@@ -898,7 +900,8 @@ function applyHiddenToUsageTerm(term, hidden) {
|
|
|
898
900
|
};
|
|
899
901
|
if (term.type === "exclusive") return {
|
|
900
902
|
type: "exclusive",
|
|
901
|
-
terms: term.terms.map((u) => applyHiddenToUsage(u, hidden))
|
|
903
|
+
terms: term.terms.map((u) => applyHiddenToUsage(u, hidden)),
|
|
904
|
+
...term.acceptsEmpty == null ? {} : { acceptsEmpty: term.acceptsEmpty }
|
|
902
905
|
};
|
|
903
906
|
if (term.type === "argument" || term.type === "option" || term.type === "command" || term.type === "passthrough") return {
|
|
904
907
|
...term,
|
|
@@ -962,7 +965,7 @@ function isOptionRequiringValue(usage, token) {
|
|
|
962
965
|
} else if (term.type === "optional" || term.type === "multiple" || term.type === "sequence") {
|
|
963
966
|
if (traverse(term.terms)) return true;
|
|
964
967
|
} else if (term.type === "exclusive") {
|
|
965
|
-
for (const exclusiveUsage of term.terms) if (traverse(exclusiveUsage)) return true;
|
|
968
|
+
for (const exclusiveUsage$1 of term.terms) if (traverse(exclusiveUsage$1)) return true;
|
|
966
969
|
}
|
|
967
970
|
return false;
|
|
968
971
|
}
|
|
@@ -1202,6 +1205,28 @@ function preserveExclusiveStateAfterOptionsTerminator(context, result) {
|
|
|
1202
1205
|
};
|
|
1203
1206
|
}
|
|
1204
1207
|
/**
|
|
1208
|
+
* Describes the branches of an exclusive combinator for the empty-input
|
|
1209
|
+
* rules.
|
|
1210
|
+
*/
|
|
1211
|
+
function getExclusiveBranches(parsers) {
|
|
1212
|
+
return parsers.map((parser) => ({
|
|
1213
|
+
facts: require_empty_input.getEmptyInputFacts(parser),
|
|
1214
|
+
matchesTokens: parser.leadingNames.size > 0 || parser.acceptingAnyToken
|
|
1215
|
+
}));
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Builds the exclusive usage term of `or()` and `longestMatch()`, recording
|
|
1219
|
+
* whether the combinator accepts an empty argument list when known.
|
|
1220
|
+
*/
|
|
1221
|
+
function exclusiveUsage(parsers, facts) {
|
|
1222
|
+
const acceptsEmpty = require_empty_input.acceptsEmptyInput(facts);
|
|
1223
|
+
return [{
|
|
1224
|
+
type: "exclusive",
|
|
1225
|
+
terms: parsers.map((p) => p.usage),
|
|
1226
|
+
...acceptsEmpty == null ? {} : { acceptsEmpty }
|
|
1227
|
+
}];
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1205
1230
|
* Creates a complete() method shared by or() and longestMatch().
|
|
1206
1231
|
* @internal
|
|
1207
1232
|
*/
|
|
@@ -1914,15 +1939,13 @@ function or(...args) {
|
|
|
1914
1939
|
if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
|
|
1915
1940
|
return error;
|
|
1916
1941
|
};
|
|
1942
|
+
const emptyInputFacts = require_empty_input.orFacts(getExclusiveBranches(parsers));
|
|
1917
1943
|
const singleResult = {
|
|
1918
1944
|
mode: combinedMode,
|
|
1919
1945
|
$valueType: [],
|
|
1920
1946
|
$stateType: [],
|
|
1921
1947
|
priority: Math.max(...parsers.map((p) => p.priority)),
|
|
1922
|
-
usage:
|
|
1923
|
-
type: "exclusive",
|
|
1924
|
-
terms: parsers.map((p) => p.usage)
|
|
1925
|
-
}],
|
|
1948
|
+
usage: exclusiveUsage(parsers, emptyInputFacts),
|
|
1926
1949
|
leadingNames: unionLeadingNames(parsers),
|
|
1927
1950
|
acceptingAnyToken: parsers.some((p) => p.acceptingAnyToken),
|
|
1928
1951
|
initialState: void 0,
|
|
@@ -1984,7 +2007,7 @@ function or(...args) {
|
|
|
1984
2007
|
defineExclusiveSchedulingNodes(singleResult, parsers);
|
|
1985
2008
|
require_internal_parser.defineInheritedAnnotationParser(singleResult);
|
|
1986
2009
|
require_passthrough.defineReachableChildren(singleResult, parsers, (state) => selectExclusiveChildren(parsers, state, true));
|
|
1987
|
-
return require_modifiers.fluent(require_short_option.scopeParser(singleResult, require_short_option.combinedOptionScope(parsers, parsers.map((_, index) => index))));
|
|
2010
|
+
return require_modifiers.fluent(require_empty_input.withEmptyInputFacts(require_short_option.scopeParser(singleResult, require_short_option.combinedOptionScope(parsers, parsers.map((_, index) => index))), emptyInputFacts));
|
|
1988
2011
|
}
|
|
1989
2012
|
/**
|
|
1990
2013
|
* @since 0.5.0
|
|
@@ -2162,15 +2185,13 @@ function createLongestMatch(...args) {
|
|
|
2162
2185
|
if (bestMatch != null) return commitLongestMatch(context, bestMatch, void 0, consumingFailure);
|
|
2163
2186
|
return error;
|
|
2164
2187
|
};
|
|
2188
|
+
const emptyInputFacts = require_empty_input.longestMatchFacts(getExclusiveBranches(parsers));
|
|
2165
2189
|
const multiResult = {
|
|
2166
2190
|
mode: combinedMode,
|
|
2167
2191
|
$valueType: [],
|
|
2168
2192
|
$stateType: [],
|
|
2169
2193
|
priority: Math.max(...parsers.map((p) => p.priority)),
|
|
2170
|
-
usage:
|
|
2171
|
-
type: "exclusive",
|
|
2172
|
-
terms: parsers.map((p) => p.usage)
|
|
2173
|
-
}],
|
|
2194
|
+
usage: exclusiveUsage(parsers, emptyInputFacts),
|
|
2174
2195
|
leadingNames: unionLeadingNames(parsers),
|
|
2175
2196
|
acceptingAnyToken: parsers.some((p) => p.acceptingAnyToken),
|
|
2176
2197
|
initialState: void 0,
|
|
@@ -2230,7 +2251,7 @@ function createLongestMatch(...args) {
|
|
|
2230
2251
|
defineExclusiveSchedulingNodes(multiResult, parsers);
|
|
2231
2252
|
require_internal_parser.defineInheritedAnnotationParser(multiResult);
|
|
2232
2253
|
require_passthrough.defineReachableChildren(multiResult, parsers, (state) => selectExclusiveChildren(parsers, state, false));
|
|
2233
|
-
return require_modifiers.fluent(require_short_option.scopeParser(multiResult, require_short_option.combinedOptionScope(parsers, parsers.map((_, index) => index))));
|
|
2254
|
+
return require_modifiers.fluent(require_empty_input.withEmptyInputFacts(require_short_option.scopeParser(multiResult, require_short_option.combinedOptionScope(parsers, parsers.map((_, index) => index))), emptyInputFacts));
|
|
2234
2255
|
}
|
|
2235
2256
|
/**
|
|
2236
2257
|
* Creates the initial parse error shared by object-like combinators.
|
|
@@ -3288,7 +3309,10 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
3288
3309
|
state: require_annotation_state.getWrappedChildParseState(state, getFieldState(field, parser), parser)
|
|
3289
3310
|
}));
|
|
3290
3311
|
});
|
|
3291
|
-
return require_modifiers.fluent(require_short_option.scopeParser(objectParser, require_short_option.combinedOptionScope(parserPairs.map(([, p]) => p), parserPairs.map(([field]) => field)), void 0, true))
|
|
3312
|
+
return require_modifiers.fluent(require_empty_input.withEmptyInputFacts(require_short_option.scopeParser(objectParser, require_short_option.combinedOptionScope(parserPairs.map(([, p]) => p), parserPairs.map(([field]) => field)), void 0, true), combinedMode === "sync" ? require_empty_input.objectFacts(parserPairs.map(([, p]) => ({
|
|
3313
|
+
facts: require_empty_input.getEmptyInputFacts(p),
|
|
3314
|
+
matchesTokens: p.leadingNames.size > 0 || p.acceptingAnyToken
|
|
3315
|
+
})), parserPairs.map(([field]) => field)) : {}));
|
|
3292
3316
|
}
|
|
3293
3317
|
function isParserLike(value) {
|
|
3294
3318
|
return value != null && (typeof value === "object" || typeof value === "function") && "parse" in value && "$valueType" in value && "$stateType" in value;
|
|
@@ -4339,7 +4363,10 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
4339
4363
|
parser,
|
|
4340
4364
|
state: require_annotation_state.getWrappedChildParseState(state, state[index], parser)
|
|
4341
4365
|
})));
|
|
4342
|
-
return require_modifiers.fluent(require_short_option.scopeParser(tupleParser, require_short_option.combinedOptionScope(parsers, parsers.map((_, index) => index))))
|
|
4366
|
+
return require_modifiers.fluent(require_empty_input.withEmptyInputFacts(require_short_option.scopeParser(tupleParser, require_short_option.combinedOptionScope(parsers, parsers.map((_, index) => index))), require_empty_input.tupleFacts(parsers.map((p) => ({
|
|
4367
|
+
facts: require_empty_input.getEmptyInputFacts(p),
|
|
4368
|
+
matchesTokens: p.leadingNames.size > 0 || p.acceptingAnyToken
|
|
4369
|
+
})))));
|
|
4343
4370
|
}
|
|
4344
4371
|
function seq(...rawArgs) {
|
|
4345
4372
|
const label = typeof rawArgs[0] === "string" ? rawArgs[0] : void 0;
|
|
@@ -6510,7 +6537,7 @@ function group(label, parser, options = {}) {
|
|
|
6510
6537
|
parser,
|
|
6511
6538
|
state
|
|
6512
6539
|
}]);
|
|
6513
|
-
return require_modifiers.fluent(require_short_option.scopeParser(groupParser, require_short_option.combinedOptionScope([parser])));
|
|
6540
|
+
return require_modifiers.fluent(require_empty_input.withEmptyInputFacts(require_short_option.scopeParser(groupParser, require_short_option.combinedOptionScope([parser])), require_empty_input.getEmptyInputFacts(parser)));
|
|
6514
6541
|
}
|
|
6515
6542
|
/**
|
|
6516
6543
|
* Creates a conditional parser that selects different branch parsers based on
|
package/dist/constructs.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createDependencySourceState, dependencyId, isDependencySourceState, isP
|
|
|
8
8
|
import { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyRuntimeContext, effectfulSchedulingNodesKey, exclusiveSourceScopeKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, forkRuntimeForCommittedSubtree, getDefaultDependencySnapshot, guaranteedStaticSourceIdsKey, hasInactiveCompletion, isMatchedState, recordSourceScope, releaseUncollectedSourceValues, replaceCachedBarrierFailure, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey } from "./dependency-runtime.js";
|
|
9
9
|
import { defineInheritedAnnotationParser, defineParseLanes, getOwnParseLanes, getParserSuggestRuntimeNodes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
|
|
10
10
|
import { annotationViewTargets, getWrappedChildParseState, getWrappedChildState, reconcileObjectChildState, unwrapAnnotationView } from "./annotation-state.js";
|
|
11
|
+
import { acceptsEmptyInput, getEmptyInputFacts, longestMatchFacts, objectFacts, orFacts, tupleFacts, withEmptyInputFacts } from "./internal/empty-input.js";
|
|
11
12
|
import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
|
|
12
13
|
import { collectLeadingCandidates } from "./usage-internals.js";
|
|
13
14
|
import { adoptOptionScope, combinedOptionScope, conditionalOptionScope, forkOptionScope, restoreOptionScope, scopeParser, selectableOptionScope, withOptionScope, withParserOptionScope } from "./short-option.js";
|
|
@@ -890,7 +891,8 @@ function applyHiddenToUsageTerm(term, hidden) {
|
|
|
890
891
|
if (term.type === "multiple") return {
|
|
891
892
|
type: "multiple",
|
|
892
893
|
terms: applyHiddenToUsage(term.terms, hidden),
|
|
893
|
-
min: term.min
|
|
894
|
+
min: term.min,
|
|
895
|
+
...term.acceptsEmpty == null ? {} : { acceptsEmpty: term.acceptsEmpty }
|
|
894
896
|
};
|
|
895
897
|
if (term.type === "sequence") return {
|
|
896
898
|
type: "sequence",
|
|
@@ -898,7 +900,8 @@ function applyHiddenToUsageTerm(term, hidden) {
|
|
|
898
900
|
};
|
|
899
901
|
if (term.type === "exclusive") return {
|
|
900
902
|
type: "exclusive",
|
|
901
|
-
terms: term.terms.map((u) => applyHiddenToUsage(u, hidden))
|
|
903
|
+
terms: term.terms.map((u) => applyHiddenToUsage(u, hidden)),
|
|
904
|
+
...term.acceptsEmpty == null ? {} : { acceptsEmpty: term.acceptsEmpty }
|
|
902
905
|
};
|
|
903
906
|
if (term.type === "argument" || term.type === "option" || term.type === "command" || term.type === "passthrough") return {
|
|
904
907
|
...term,
|
|
@@ -962,7 +965,7 @@ function isOptionRequiringValue(usage, token) {
|
|
|
962
965
|
} else if (term.type === "optional" || term.type === "multiple" || term.type === "sequence") {
|
|
963
966
|
if (traverse(term.terms)) return true;
|
|
964
967
|
} else if (term.type === "exclusive") {
|
|
965
|
-
for (const exclusiveUsage of term.terms) if (traverse(exclusiveUsage)) return true;
|
|
968
|
+
for (const exclusiveUsage$1 of term.terms) if (traverse(exclusiveUsage$1)) return true;
|
|
966
969
|
}
|
|
967
970
|
return false;
|
|
968
971
|
}
|
|
@@ -1202,6 +1205,28 @@ function preserveExclusiveStateAfterOptionsTerminator(context, result) {
|
|
|
1202
1205
|
};
|
|
1203
1206
|
}
|
|
1204
1207
|
/**
|
|
1208
|
+
* Describes the branches of an exclusive combinator for the empty-input
|
|
1209
|
+
* rules.
|
|
1210
|
+
*/
|
|
1211
|
+
function getExclusiveBranches(parsers) {
|
|
1212
|
+
return parsers.map((parser) => ({
|
|
1213
|
+
facts: getEmptyInputFacts(parser),
|
|
1214
|
+
matchesTokens: parser.leadingNames.size > 0 || parser.acceptingAnyToken
|
|
1215
|
+
}));
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Builds the exclusive usage term of `or()` and `longestMatch()`, recording
|
|
1219
|
+
* whether the combinator accepts an empty argument list when known.
|
|
1220
|
+
*/
|
|
1221
|
+
function exclusiveUsage(parsers, facts) {
|
|
1222
|
+
const acceptsEmpty = acceptsEmptyInput(facts);
|
|
1223
|
+
return [{
|
|
1224
|
+
type: "exclusive",
|
|
1225
|
+
terms: parsers.map((p) => p.usage),
|
|
1226
|
+
...acceptsEmpty == null ? {} : { acceptsEmpty }
|
|
1227
|
+
}];
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1205
1230
|
* Creates a complete() method shared by or() and longestMatch().
|
|
1206
1231
|
* @internal
|
|
1207
1232
|
*/
|
|
@@ -1914,15 +1939,13 @@ function or(...args) {
|
|
|
1914
1939
|
if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
|
|
1915
1940
|
return error;
|
|
1916
1941
|
};
|
|
1942
|
+
const emptyInputFacts = orFacts(getExclusiveBranches(parsers));
|
|
1917
1943
|
const singleResult = {
|
|
1918
1944
|
mode: combinedMode,
|
|
1919
1945
|
$valueType: [],
|
|
1920
1946
|
$stateType: [],
|
|
1921
1947
|
priority: Math.max(...parsers.map((p) => p.priority)),
|
|
1922
|
-
usage:
|
|
1923
|
-
type: "exclusive",
|
|
1924
|
-
terms: parsers.map((p) => p.usage)
|
|
1925
|
-
}],
|
|
1948
|
+
usage: exclusiveUsage(parsers, emptyInputFacts),
|
|
1926
1949
|
leadingNames: unionLeadingNames(parsers),
|
|
1927
1950
|
acceptingAnyToken: parsers.some((p) => p.acceptingAnyToken),
|
|
1928
1951
|
initialState: void 0,
|
|
@@ -1984,7 +2007,7 @@ function or(...args) {
|
|
|
1984
2007
|
defineExclusiveSchedulingNodes(singleResult, parsers);
|
|
1985
2008
|
defineInheritedAnnotationParser(singleResult);
|
|
1986
2009
|
defineReachableChildren(singleResult, parsers, (state) => selectExclusiveChildren(parsers, state, true));
|
|
1987
|
-
return fluent(scopeParser(singleResult, combinedOptionScope(parsers, parsers.map((_, index) => index))));
|
|
2010
|
+
return fluent(withEmptyInputFacts(scopeParser(singleResult, combinedOptionScope(parsers, parsers.map((_, index) => index))), emptyInputFacts));
|
|
1988
2011
|
}
|
|
1989
2012
|
/**
|
|
1990
2013
|
* @since 0.5.0
|
|
@@ -2162,15 +2185,13 @@ function createLongestMatch(...args) {
|
|
|
2162
2185
|
if (bestMatch != null) return commitLongestMatch(context, bestMatch, void 0, consumingFailure);
|
|
2163
2186
|
return error;
|
|
2164
2187
|
};
|
|
2188
|
+
const emptyInputFacts = longestMatchFacts(getExclusiveBranches(parsers));
|
|
2165
2189
|
const multiResult = {
|
|
2166
2190
|
mode: combinedMode,
|
|
2167
2191
|
$valueType: [],
|
|
2168
2192
|
$stateType: [],
|
|
2169
2193
|
priority: Math.max(...parsers.map((p) => p.priority)),
|
|
2170
|
-
usage:
|
|
2171
|
-
type: "exclusive",
|
|
2172
|
-
terms: parsers.map((p) => p.usage)
|
|
2173
|
-
}],
|
|
2194
|
+
usage: exclusiveUsage(parsers, emptyInputFacts),
|
|
2174
2195
|
leadingNames: unionLeadingNames(parsers),
|
|
2175
2196
|
acceptingAnyToken: parsers.some((p) => p.acceptingAnyToken),
|
|
2176
2197
|
initialState: void 0,
|
|
@@ -2230,7 +2251,7 @@ function createLongestMatch(...args) {
|
|
|
2230
2251
|
defineExclusiveSchedulingNodes(multiResult, parsers);
|
|
2231
2252
|
defineInheritedAnnotationParser(multiResult);
|
|
2232
2253
|
defineReachableChildren(multiResult, parsers, (state) => selectExclusiveChildren(parsers, state, false));
|
|
2233
|
-
return fluent(scopeParser(multiResult, combinedOptionScope(parsers, parsers.map((_, index) => index))));
|
|
2254
|
+
return fluent(withEmptyInputFacts(scopeParser(multiResult, combinedOptionScope(parsers, parsers.map((_, index) => index))), emptyInputFacts));
|
|
2234
2255
|
}
|
|
2235
2256
|
/**
|
|
2236
2257
|
* Creates the initial parse error shared by object-like combinators.
|
|
@@ -3288,7 +3309,10 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
3288
3309
|
state: getWrappedChildParseState(state, getFieldState(field, parser), parser)
|
|
3289
3310
|
}));
|
|
3290
3311
|
});
|
|
3291
|
-
return fluent(scopeParser(objectParser, combinedOptionScope(parserPairs.map(([, p]) => p), parserPairs.map(([field]) => field)), void 0, true))
|
|
3312
|
+
return fluent(withEmptyInputFacts(scopeParser(objectParser, combinedOptionScope(parserPairs.map(([, p]) => p), parserPairs.map(([field]) => field)), void 0, true), combinedMode === "sync" ? objectFacts(parserPairs.map(([, p]) => ({
|
|
3313
|
+
facts: getEmptyInputFacts(p),
|
|
3314
|
+
matchesTokens: p.leadingNames.size > 0 || p.acceptingAnyToken
|
|
3315
|
+
})), parserPairs.map(([field]) => field)) : {}));
|
|
3292
3316
|
}
|
|
3293
3317
|
function isParserLike(value) {
|
|
3294
3318
|
return value != null && (typeof value === "object" || typeof value === "function") && "parse" in value && "$valueType" in value && "$stateType" in value;
|
|
@@ -4339,7 +4363,10 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
4339
4363
|
parser,
|
|
4340
4364
|
state: getWrappedChildParseState(state, state[index], parser)
|
|
4341
4365
|
})));
|
|
4342
|
-
return fluent(scopeParser(tupleParser, combinedOptionScope(parsers, parsers.map((_, index) => index))))
|
|
4366
|
+
return fluent(withEmptyInputFacts(scopeParser(tupleParser, combinedOptionScope(parsers, parsers.map((_, index) => index))), tupleFacts(parsers.map((p) => ({
|
|
4367
|
+
facts: getEmptyInputFacts(p),
|
|
4368
|
+
matchesTokens: p.leadingNames.size > 0 || p.acceptingAnyToken
|
|
4369
|
+
})))));
|
|
4343
4370
|
}
|
|
4344
4371
|
function seq(...rawArgs) {
|
|
4345
4372
|
const label = typeof rawArgs[0] === "string" ? rawArgs[0] : void 0;
|
|
@@ -6510,7 +6537,7 @@ function group(label, parser, options = {}) {
|
|
|
6510
6537
|
parser,
|
|
6511
6538
|
state
|
|
6512
6539
|
}]);
|
|
6513
|
-
return fluent(scopeParser(groupParser, combinedOptionScope([parser])));
|
|
6540
|
+
return fluent(withEmptyInputFacts(scopeParser(groupParser, combinedOptionScope([parser])), getEmptyInputFacts(parser)));
|
|
6514
6541
|
}
|
|
6515
6542
|
/**
|
|
6516
6543
|
* Creates a conditional parser that selects different branch parsers based on
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/internal/empty-input.ts
|
|
3
|
+
/**
|
|
4
|
+
* The state of an empty step that leaves its input state unchanged.
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
const UNCHANGED = Object.freeze({ unchanged: true });
|
|
8
|
+
/**
|
|
9
|
+
* Returns the state that an empty step produces from the given initial
|
|
10
|
+
* state.
|
|
11
|
+
* @param next The step's resulting state.
|
|
12
|
+
* @param initialState The state the step started from.
|
|
13
|
+
* @returns The resulting state.
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
function resolveEmptyStepState(next, initialState) {
|
|
17
|
+
return "unchanged" in next ? initialState : next.state(initialState);
|
|
18
|
+
}
|
|
19
|
+
const emptyInputFactsKey = Symbol("emptyInputFacts");
|
|
20
|
+
const UNKNOWN = Object.freeze({});
|
|
21
|
+
/**
|
|
22
|
+
* Attaches empty-input facts to a parser. The facts are stored as a
|
|
23
|
+
* non-enumerable property, so object spread does not copy them, and they
|
|
24
|
+
* are bound to the parser's current `parse`, `complete`, and
|
|
25
|
+
* `initialState`, so replacing any of those invalidates them.
|
|
26
|
+
* @param parser The parser to annotate.
|
|
27
|
+
* @param facts The facts to attach.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
function defineEmptyInputFacts(parser, facts) {
|
|
31
|
+
if (Object.keys(facts).length < 1) return;
|
|
32
|
+
Object.defineProperty(parser, emptyInputFactsKey, {
|
|
33
|
+
value: {
|
|
34
|
+
facts,
|
|
35
|
+
parse: parser.parse,
|
|
36
|
+
complete: parser.complete,
|
|
37
|
+
...Object.getOwnPropertyDescriptor(parser, "initialState")?.get == null ? { initialState: { value: parser.initialState } } : {}
|
|
38
|
+
},
|
|
39
|
+
configurable: true,
|
|
40
|
+
enumerable: false,
|
|
41
|
+
writable: false
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Returns the empty-input facts of a parser, or an empty object when the
|
|
46
|
+
* facts are unknown.
|
|
47
|
+
* @param parser The parser to inspect.
|
|
48
|
+
* @returns The facts known for the parser.
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
function getEmptyInputFacts(parser) {
|
|
52
|
+
const stored = parser[emptyInputFactsKey];
|
|
53
|
+
if (stored == null || stored.parse !== parser.parse || stored.complete !== parser.complete || stored.initialState != null && stored.initialState.value !== parser.initialState) return UNKNOWN;
|
|
54
|
+
return stored.facts;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Returns whether `parse(parser, [])` succeeds, when that is known from the
|
|
58
|
+
* facts.
|
|
59
|
+
* @param facts The parser's facts.
|
|
60
|
+
* @returns `true` or `false` when known, `undefined` otherwise.
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
function acceptsEmptyInput(facts) {
|
|
64
|
+
if (facts.step === "failure") return false;
|
|
65
|
+
if (facts.step == null) return void 0;
|
|
66
|
+
return facts.afterStep;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Returns the facts of a parser that fails its empty step and whose
|
|
70
|
+
* completion from the initial state has the given outcome.
|
|
71
|
+
* @param fromInitial Whether completion from the initial state succeeds.
|
|
72
|
+
* @returns The facts.
|
|
73
|
+
* @internal
|
|
74
|
+
*/
|
|
75
|
+
function failingStepFacts(fromInitial) {
|
|
76
|
+
return {
|
|
77
|
+
step: "failure",
|
|
78
|
+
fromInitial
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Returns whether `complete()` succeeds after the parser was offered an
|
|
83
|
+
* empty step whose state change was kept (on success) or discarded (on
|
|
84
|
+
* failure).
|
|
85
|
+
* @param facts The parser's facts.
|
|
86
|
+
* @returns The outcome, or `undefined` when unknown.
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
function completionAfterOptionalStep(facts) {
|
|
90
|
+
if (facts.step === "failure") return facts.fromInitial;
|
|
91
|
+
if (facts.step == null) return void 0;
|
|
92
|
+
return facts.afterStep;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The state of a wrapper that stores its child's resulting state as the
|
|
96
|
+
* only element of a fresh array.
|
|
97
|
+
*/
|
|
98
|
+
function wrapInFreshArray(child, childInitialState) {
|
|
99
|
+
return { state: () => [resolveEmptyStepState(child, childInitialState)] };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Computes the facts of `optional(p)` and `withDefault(p, value)` when
|
|
103
|
+
* completing the wrapper from its initial state is known to succeed.
|
|
104
|
+
* @param child The facts of the wrapped parser.
|
|
105
|
+
* @param childInitialState The wrapped parser's initial state.
|
|
106
|
+
* @param fromInitial Whether completing the wrapper from its initial state
|
|
107
|
+
* succeeds, or `undefined` when unknown.
|
|
108
|
+
* @returns The facts of the wrapper.
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
function optionalLikeFacts(child, childInitialState, fromInitial) {
|
|
112
|
+
if (child.step === "failure") return {
|
|
113
|
+
step: "success",
|
|
114
|
+
next: UNCHANGED,
|
|
115
|
+
afterStep: fromInitial,
|
|
116
|
+
fromInitial
|
|
117
|
+
};
|
|
118
|
+
if (child.step == null) return { fromInitial };
|
|
119
|
+
return {
|
|
120
|
+
step: child.step,
|
|
121
|
+
...child.next == null ? {} : { next: wrapInFreshArray(child.next, childInitialState) },
|
|
122
|
+
afterStep: child.afterStep,
|
|
123
|
+
fromInitial
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Computes the facts of `multiple(p, { min })` without a `max` bound.
|
|
128
|
+
* @param child The facts of the repeated parser.
|
|
129
|
+
* @param retainsItem Whether a successful empty step of the child is kept
|
|
130
|
+
* as a repetition item, or `undefined` when unknown.
|
|
131
|
+
* @param childInitialState The repeated parser's initial state.
|
|
132
|
+
* @param min The minimum number of items.
|
|
133
|
+
* @returns The facts of the repetition.
|
|
134
|
+
* @internal
|
|
135
|
+
*/
|
|
136
|
+
function multipleFacts(child, retainsItem, childInitialState, min) {
|
|
137
|
+
const fromInitial = min < 1;
|
|
138
|
+
if (child.step === "failure") return min < 1 ? {
|
|
139
|
+
step: "success",
|
|
140
|
+
next: UNCHANGED,
|
|
141
|
+
afterStep: true,
|
|
142
|
+
fromInitial
|
|
143
|
+
} : {
|
|
144
|
+
step: "failure",
|
|
145
|
+
fromInitial
|
|
146
|
+
};
|
|
147
|
+
if (child.step == null || retainsItem == null) return { fromInitial };
|
|
148
|
+
if (!retainsItem) return {
|
|
149
|
+
step: "success",
|
|
150
|
+
next: UNCHANGED,
|
|
151
|
+
afterStep: min < 1,
|
|
152
|
+
fromInitial
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
step: child.step,
|
|
156
|
+
...child.next == null ? {} : { next: wrapInFreshArray(child.next, childInitialState) },
|
|
157
|
+
afterStep: child.afterStep == null ? void 0 : min <= 1 && child.afterStep,
|
|
158
|
+
fromInitial
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function selectZeroInputCandidate(branches) {
|
|
162
|
+
let index = -1;
|
|
163
|
+
let count = 0;
|
|
164
|
+
for (let i = 0; i < branches.length; i++) {
|
|
165
|
+
const branch = branches[i];
|
|
166
|
+
if (branch.matchesTokens) continue;
|
|
167
|
+
if (branch.facts.step == null) return void 0;
|
|
168
|
+
if (branch.facts.step !== "success") continue;
|
|
169
|
+
if (index < 0) index = i;
|
|
170
|
+
count++;
|
|
171
|
+
}
|
|
172
|
+
return count === 1 ? { index } : null;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Computes the completion of `or()` and `longestMatch()` from their initial
|
|
176
|
+
* state, which selects the unique zero-input candidate among the branches
|
|
177
|
+
* that cannot match tokens.
|
|
178
|
+
*/
|
|
179
|
+
function exclusiveFromInitial(branches) {
|
|
180
|
+
const candidate = selectZeroInputCandidate(branches);
|
|
181
|
+
if (candidate === void 0) return void 0;
|
|
182
|
+
if (candidate === null) return false;
|
|
183
|
+
return branches[candidate.index].facts.afterStep;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* An exemplar of an exclusive combinator's state after it selected a branch.
|
|
187
|
+
* It is a fresh, non-nullish value, which is all that repetition item checks
|
|
188
|
+
* look at.
|
|
189
|
+
*/
|
|
190
|
+
function exclusiveNextState(index) {
|
|
191
|
+
return { state: () => [index, {
|
|
192
|
+
success: true,
|
|
193
|
+
consumed: []
|
|
194
|
+
}] };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Computes the facts of `or()`.
|
|
198
|
+
* @param branches The branches of the exclusive combinator.
|
|
199
|
+
* @returns The facts of the combinator.
|
|
200
|
+
* @internal
|
|
201
|
+
*/
|
|
202
|
+
function orFacts(branches) {
|
|
203
|
+
const candidate = selectZeroInputCandidate(branches);
|
|
204
|
+
if (candidate === void 0) return {};
|
|
205
|
+
const fromInitial = exclusiveFromInitial(branches);
|
|
206
|
+
if (candidate === null) return {
|
|
207
|
+
step: "failure",
|
|
208
|
+
fromInitial
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
step: "success",
|
|
212
|
+
next: exclusiveNextState(candidate.index),
|
|
213
|
+
afterStep: branches[candidate.index].facts.afterStep,
|
|
214
|
+
fromInitial
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Computes the facts of `longestMatch()`, which selects the first branch
|
|
219
|
+
* whose empty step succeeds, preferring a definitive success over a
|
|
220
|
+
* provisional one.
|
|
221
|
+
* @param branches The branches of the exclusive combinator.
|
|
222
|
+
* @returns The facts of the combinator.
|
|
223
|
+
* @internal
|
|
224
|
+
*/
|
|
225
|
+
function longestMatchFacts(branches) {
|
|
226
|
+
const fromInitial = exclusiveFromInitial(branches);
|
|
227
|
+
let best = null;
|
|
228
|
+
for (let i = 0; i < branches.length; i++) {
|
|
229
|
+
const facts = branches[i].facts;
|
|
230
|
+
if (facts.step == null) return { fromInitial };
|
|
231
|
+
if (facts.step === "failure") continue;
|
|
232
|
+
if (best == null || best.facts.step === "provisional" && facts.step === "success") best = {
|
|
233
|
+
index: i,
|
|
234
|
+
facts
|
|
235
|
+
};
|
|
236
|
+
if (best.facts.step === "success") break;
|
|
237
|
+
}
|
|
238
|
+
if (best == null) return {
|
|
239
|
+
step: "failure",
|
|
240
|
+
fromInitial
|
|
241
|
+
};
|
|
242
|
+
return {
|
|
243
|
+
step: best.facts.step,
|
|
244
|
+
next: exclusiveNextState(best.index),
|
|
245
|
+
afterStep: best.facts.afterStep,
|
|
246
|
+
fromInitial
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function all(values) {
|
|
250
|
+
let unknown = false;
|
|
251
|
+
for (const value of values) {
|
|
252
|
+
if (value === false) return false;
|
|
253
|
+
if (value == null) unknown = true;
|
|
254
|
+
}
|
|
255
|
+
return unknown ? void 0 : true;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Computes the facts of `tuple()`. On an empty buffer `tuple()` offers
|
|
259
|
+
* each child one zero-token step, stores the state of every child whose
|
|
260
|
+
* step succeeded in a fresh state array, and skips the rest.
|
|
261
|
+
* @param children The children in declaration order.
|
|
262
|
+
* @returns The facts of the tuple.
|
|
263
|
+
* @internal
|
|
264
|
+
*/
|
|
265
|
+
function tupleFacts(children) {
|
|
266
|
+
const fromInitial = all(children.map((c) => c.facts.fromInitial));
|
|
267
|
+
if (children.some((c) => c.facts.step == null)) return {
|
|
268
|
+
step: "success",
|
|
269
|
+
fromInitial
|
|
270
|
+
};
|
|
271
|
+
const afterStep = all(children.map((c) => completionAfterOptionalStep(c.facts)));
|
|
272
|
+
let next;
|
|
273
|
+
if (children.every((c) => c.facts.step === "failure")) next = UNCHANGED;
|
|
274
|
+
else if (children.every((c) => c.facts.step === "failure" || c.facts.next != null)) {
|
|
275
|
+
const steps = children.map((c) => c.facts.step === "failure" ? void 0 : c.facts.next);
|
|
276
|
+
next = { state: (initialState) => {
|
|
277
|
+
const slots = Array.isArray(initialState) ? initialState : [];
|
|
278
|
+
return steps.map((step, i) => step == null ? slots[i] : resolveEmptyStepState(step, slots[i]));
|
|
279
|
+
} };
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
step: "success",
|
|
283
|
+
...next == null ? {} : { next },
|
|
284
|
+
afterStep,
|
|
285
|
+
fromInitial
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* The state of `object()` after an empty step: a committed field state makes
|
|
290
|
+
* it store a fresh record with the other fields unchanged.
|
|
291
|
+
*/
|
|
292
|
+
function objectNextState(changed) {
|
|
293
|
+
if (changed.length < 1) return UNCHANGED;
|
|
294
|
+
return { state: (initialState) => {
|
|
295
|
+
const record = initialState != null && typeof initialState === "object" ? { ...initialState } : {};
|
|
296
|
+
for (const [key, next] of changed) record[key] = resolveEmptyStepState(next, record[key]);
|
|
297
|
+
return record;
|
|
298
|
+
} };
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Computes the facts of a synchronous `object()`. On an empty buffer
|
|
302
|
+
* `object()` offers each field that cannot match tokens one zero-token
|
|
303
|
+
* step, keeps the resulting state when it changed, and then succeeds only
|
|
304
|
+
* if every field can complete.
|
|
305
|
+
* @param children The fields.
|
|
306
|
+
* @param keys The field keys, parallel to `children`.
|
|
307
|
+
* @returns The facts of the object.
|
|
308
|
+
* @internal
|
|
309
|
+
*/
|
|
310
|
+
function objectFacts(children, keys) {
|
|
311
|
+
const fromInitial = all(children.map((c) => c.facts.fromInitial));
|
|
312
|
+
const outcomes = [];
|
|
313
|
+
const changed = [];
|
|
314
|
+
let nextKnown = true;
|
|
315
|
+
for (const [i, child] of children.entries()) {
|
|
316
|
+
const { step, next } = child.facts;
|
|
317
|
+
if (child.matchesTokens || step === "failure") outcomes.push(child.facts.fromInitial);
|
|
318
|
+
else if (step == null) {
|
|
319
|
+
outcomes.push(void 0);
|
|
320
|
+
nextKnown = false;
|
|
321
|
+
} else if (next == null) {
|
|
322
|
+
nextKnown = false;
|
|
323
|
+
outcomes.push(child.facts.afterStep === child.facts.fromInitial ? child.facts.afterStep : void 0);
|
|
324
|
+
} else if ("unchanged" in next) outcomes.push(child.facts.fromInitial);
|
|
325
|
+
else {
|
|
326
|
+
changed.push([keys[i], next]);
|
|
327
|
+
outcomes.push(child.facts.afterStep);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const accepted = all(outcomes);
|
|
331
|
+
if (accepted == null) return { fromInitial };
|
|
332
|
+
if (!accepted) return {
|
|
333
|
+
step: "failure",
|
|
334
|
+
fromInitial
|
|
335
|
+
};
|
|
336
|
+
return {
|
|
337
|
+
step: "success",
|
|
338
|
+
...nextKnown ? { next: objectNextState(changed) } : {},
|
|
339
|
+
afterStep: true,
|
|
340
|
+
fromInitial
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Attaches empty-input facts to a parser and returns the parser.
|
|
345
|
+
* @param parser The parser to annotate.
|
|
346
|
+
* @param facts The facts to attach.
|
|
347
|
+
* @returns The same parser.
|
|
348
|
+
* @internal
|
|
349
|
+
*/
|
|
350
|
+
function withEmptyInputFacts(parser, facts) {
|
|
351
|
+
defineEmptyInputFacts(parser, facts);
|
|
352
|
+
return parser;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
//#endregion
|
|
356
|
+
exports.UNCHANGED = UNCHANGED;
|
|
357
|
+
exports.acceptsEmptyInput = acceptsEmptyInput;
|
|
358
|
+
exports.defineEmptyInputFacts = defineEmptyInputFacts;
|
|
359
|
+
exports.failingStepFacts = failingStepFacts;
|
|
360
|
+
exports.getEmptyInputFacts = getEmptyInputFacts;
|
|
361
|
+
exports.longestMatchFacts = longestMatchFacts;
|
|
362
|
+
exports.multipleFacts = multipleFacts;
|
|
363
|
+
exports.objectFacts = objectFacts;
|
|
364
|
+
exports.optionalLikeFacts = optionalLikeFacts;
|
|
365
|
+
exports.orFacts = orFacts;
|
|
366
|
+
exports.resolveEmptyStepState = resolveEmptyStepState;
|
|
367
|
+
exports.tupleFacts = tupleFacts;
|
|
368
|
+
exports.withEmptyInputFacts = withEmptyInputFacts;
|