@optique/core 0.3.0-dev.40 → 0.3.0-dev.42

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/facade.cjs CHANGED
@@ -38,22 +38,48 @@ function run(parser, programName, args, options = {}) {
38
38
  let { colors, maxWidth, help = "none", onHelp = () => {}, aboveError = "usage", onError = () => {
39
39
  throw new RunError("Failed to parse command line arguments.");
40
40
  }, stderr = console.error, stdout = console.log } = options;
41
+ const contextualHelpParser = require_parser.object({
42
+ help: require_parser.constant(true),
43
+ commands: require_parser.multiple(require_parser.argument(require_valueparser.string({ metavar: "COMMAND" }))),
44
+ __help: require_parser.flag("--help")
45
+ });
41
46
  const helpCommand = require_parser.command("help", require_parser.multiple(require_parser.argument(require_valueparser.string({ metavar: "COMMAND" }))), { description: require_message.message`Show help information.` });
42
47
  const helpOption = require_parser.option("--help", { description: require_message.message`Show help information.` });
43
- const augmentedParser = help === "none" ? require_parser.object({
48
+ const augmentedParser = help === "none" ? parser : help === "command" ? require_parser.longestMatch(require_parser.object({
49
+ help: require_parser.constant(true),
50
+ commands: helpCommand
51
+ }), require_parser.object({
44
52
  help: require_parser.constant(false),
45
53
  result: parser
46
- }) : require_parser.or(require_parser.object({
54
+ })) : help === "option" ? require_parser.longestMatch(require_parser.object({
55
+ help: require_parser.constant(false),
56
+ result: parser
57
+ }), contextualHelpParser, require_parser.merge(require_parser.object({
58
+ help: require_parser.constant(true),
59
+ commands: require_parser.constant([])
60
+ }), helpOption)) : require_parser.longestMatch(require_parser.object({
47
61
  help: require_parser.constant(false),
48
62
  result: parser
49
63
  }), require_parser.object({
50
64
  help: require_parser.constant(true),
51
- command: help === "both" ? require_parser.or(helpCommand, helpOption) : help === "command" ? helpCommand : helpOption
52
- }));
65
+ commands: helpCommand
66
+ }), contextualHelpParser, require_parser.merge(require_parser.object({
67
+ help: require_parser.constant(true),
68
+ commands: require_parser.constant([])
69
+ }), helpOption));
53
70
  const result = require_parser.parse(augmentedParser, args);
54
71
  if (result.success) {
55
- if (!result.value.help) return result.value.result;
56
- const doc = require_parser.getDocPage(typeof result.value.command === "boolean" || result.value.command.length < 1 ? augmentedParser : parser, typeof result.value.command === "boolean" ? [] : result.value.command);
72
+ const value = result.value;
73
+ if (help === "none") return value;
74
+ if (typeof value === "object" && value != null && "help" in value) {
75
+ const helpValue$1 = value;
76
+ if (!helpValue$1.help) return helpValue$1.result;
77
+ } else return value;
78
+ let commandContext = [];
79
+ const helpValue = value;
80
+ if (Array.isArray(helpValue.commands)) commandContext = helpValue.commands;
81
+ else if (typeof helpValue.commands === "object" && helpValue.commands != null && "length" in helpValue.commands) commandContext = helpValue.commands;
82
+ const doc = require_parser.getDocPage(commandContext.length < 1 ? augmentedParser : parser, commandContext);
57
83
  if (doc != null) stdout(require_doc.formatDocPage(programName, doc, {
58
84
  colors,
59
85
  maxWidth
package/dist/facade.js CHANGED
@@ -2,7 +2,7 @@ import { formatMessage, message } from "./message.js";
2
2
  import { formatUsage } from "./usage.js";
3
3
  import { formatDocPage } from "./doc.js";
4
4
  import { string } from "./valueparser.js";
5
- import { argument, command, constant, getDocPage, multiple, object, option, or, parse } from "./parser.js";
5
+ import { argument, command, constant, flag, getDocPage, longestMatch, merge, multiple, object, option, parse } from "./parser.js";
6
6
 
7
7
  //#region src/facade.ts
8
8
  /**
@@ -38,22 +38,48 @@ function run(parser, programName, args, options = {}) {
38
38
  let { colors, maxWidth, help = "none", onHelp = () => {}, aboveError = "usage", onError = () => {
39
39
  throw new RunError("Failed to parse command line arguments.");
40
40
  }, stderr = console.error, stdout = console.log } = options;
41
+ const contextualHelpParser = object({
42
+ help: constant(true),
43
+ commands: multiple(argument(string({ metavar: "COMMAND" }))),
44
+ __help: flag("--help")
45
+ });
41
46
  const helpCommand = command("help", multiple(argument(string({ metavar: "COMMAND" }))), { description: message`Show help information.` });
42
47
  const helpOption = option("--help", { description: message`Show help information.` });
43
- const augmentedParser = help === "none" ? object({
48
+ const augmentedParser = help === "none" ? parser : help === "command" ? longestMatch(object({
49
+ help: constant(true),
50
+ commands: helpCommand
51
+ }), object({
44
52
  help: constant(false),
45
53
  result: parser
46
- }) : or(object({
54
+ })) : help === "option" ? longestMatch(object({
55
+ help: constant(false),
56
+ result: parser
57
+ }), contextualHelpParser, merge(object({
58
+ help: constant(true),
59
+ commands: constant([])
60
+ }), helpOption)) : longestMatch(object({
47
61
  help: constant(false),
48
62
  result: parser
49
63
  }), object({
50
64
  help: constant(true),
51
- command: help === "both" ? or(helpCommand, helpOption) : help === "command" ? helpCommand : helpOption
52
- }));
65
+ commands: helpCommand
66
+ }), contextualHelpParser, merge(object({
67
+ help: constant(true),
68
+ commands: constant([])
69
+ }), helpOption));
53
70
  const result = parse(augmentedParser, args);
54
71
  if (result.success) {
55
- if (!result.value.help) return result.value.result;
56
- const doc = getDocPage(typeof result.value.command === "boolean" || result.value.command.length < 1 ? augmentedParser : parser, typeof result.value.command === "boolean" ? [] : result.value.command);
72
+ const value = result.value;
73
+ if (help === "none") return value;
74
+ if (typeof value === "object" && value != null && "help" in value) {
75
+ const helpValue$1 = value;
76
+ if (!helpValue$1.help) return helpValue$1.result;
77
+ } else return value;
78
+ let commandContext = [];
79
+ const helpValue = value;
80
+ if (Array.isArray(helpValue.commands)) commandContext = helpValue.commands;
81
+ else if (typeof helpValue.commands === "object" && helpValue.commands != null && "length" in helpValue.commands) commandContext = helpValue.commands;
82
+ const doc = getDocPage(commandContext.length < 1 ? augmentedParser : parser, commandContext);
57
83
  if (doc != null) stdout(formatDocPage(programName, doc, {
58
84
  colors,
59
85
  maxWidth
package/dist/index.cjs CHANGED
@@ -21,6 +21,7 @@ exports.getDocPage = require_parser.getDocPage;
21
21
  exports.integer = require_valueparser.integer;
22
22
  exports.isValueParser = require_valueparser.isValueParser;
23
23
  exports.locale = require_valueparser.locale;
24
+ exports.longestMatch = require_parser.longestMatch;
24
25
  exports.map = require_parser.map;
25
26
  exports.merge = require_parser.merge;
26
27
  exports.message = require_message.message;
package/dist/index.d.cts CHANGED
@@ -2,6 +2,6 @@ import { Message, MessageFormatOptions, MessageTerm, formatMessage, message, met
2
2
  import { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.cjs";
3
3
  import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, formatDocPage } from "./doc.cjs";
4
4
  import { ChoiceOptions, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.cjs";
5
- import { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.cjs";
5
+ import { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.cjs";
6
6
  import { RunError, RunOptions, run } from "./facade.cjs";
7
- export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
7
+ export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,6 @@ import { Message, MessageFormatOptions, MessageTerm, formatMessage, message, met
2
2
  import { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
3
3
  import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, formatDocPage } from "./doc.js";
4
4
  import { ChoiceOptions, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.js";
5
- import { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
5
+ import { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
6
6
  import { RunError, RunOptions, run } from "./facade.js";
7
- export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
7
+ export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { formatMessage, message, metavar, optionName, optionNames, text, value,
2
2
  import { formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
3
3
  import { formatDocPage } from "./doc.js";
4
4
  import { choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.js";
5
- import { argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
5
+ import { argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
6
6
  import { RunError, run } from "./facade.js";
7
7
 
8
- export { RunError, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
8
+ export { RunError, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
package/dist/parser.cjs CHANGED
@@ -1031,6 +1031,86 @@ function or(...parsers) {
1031
1031
  }
1032
1032
  };
1033
1033
  }
1034
+ function longestMatch(...parsers) {
1035
+ return {
1036
+ $valueType: [],
1037
+ $stateType: [],
1038
+ priority: Math.max(...parsers.map((p) => p.priority)),
1039
+ usage: [{
1040
+ type: "exclusive",
1041
+ terms: parsers.map((p) => p.usage)
1042
+ }],
1043
+ initialState: void 0,
1044
+ complete(state) {
1045
+ if (state == null) return {
1046
+ success: false,
1047
+ error: require_message.message`No parser matched.`
1048
+ };
1049
+ const [i, result] = state;
1050
+ if (result.success) return parsers[i].complete(result.next.state);
1051
+ return {
1052
+ success: false,
1053
+ error: result.error
1054
+ };
1055
+ },
1056
+ parse(context) {
1057
+ let bestMatch = null;
1058
+ let error = {
1059
+ consumed: 0,
1060
+ error: context.buffer.length < 1 ? require_message.message`No parser matched.` : require_message.message`Unexpected option or subcommand: ${require_message.optionName(context.buffer[0])}.`
1061
+ };
1062
+ for (let i = 0; i < parsers.length; i++) {
1063
+ const parser = parsers[i];
1064
+ const result = parser.parse({
1065
+ ...context,
1066
+ state: context.state == null || context.state[0] !== i || !context.state[1].success ? parser.initialState : context.state[1].next.state
1067
+ });
1068
+ if (result.success) {
1069
+ const consumed = context.buffer.length - result.next.buffer.length;
1070
+ if (bestMatch === null || consumed > bestMatch.consumed) bestMatch = {
1071
+ index: i,
1072
+ result,
1073
+ consumed
1074
+ };
1075
+ } else if (error.consumed < result.consumed) error = result;
1076
+ }
1077
+ if (bestMatch && bestMatch.result.success) return {
1078
+ success: true,
1079
+ next: {
1080
+ ...context,
1081
+ buffer: bestMatch.result.next.buffer,
1082
+ optionsTerminated: bestMatch.result.next.optionsTerminated,
1083
+ state: [bestMatch.index, bestMatch.result]
1084
+ },
1085
+ consumed: bestMatch.result.consumed
1086
+ };
1087
+ return {
1088
+ ...error,
1089
+ success: false
1090
+ };
1091
+ },
1092
+ getDocFragments(state, _defaultValue) {
1093
+ let description;
1094
+ let fragments;
1095
+ if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
1096
+ else {
1097
+ const [i, result] = state.state;
1098
+ if (result.success) {
1099
+ const docResult = parsers[i].getDocFragments({
1100
+ kind: "available",
1101
+ state: result.next.state
1102
+ });
1103
+ description = docResult.description;
1104
+ fragments = docResult.fragments;
1105
+ } else fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
1106
+ }
1107
+ return {
1108
+ description,
1109
+ fragments
1110
+ };
1111
+ }
1112
+ };
1113
+ }
1034
1114
  function merge(...parsers) {
1035
1115
  parsers = parsers.toSorted((a, b) => b.priority - a.priority);
1036
1116
  const initialState = {};
@@ -1483,6 +1563,7 @@ exports.concat = concat;
1483
1563
  exports.constant = constant;
1484
1564
  exports.flag = flag;
1485
1565
  exports.getDocPage = getDocPage;
1566
+ exports.longestMatch = longestMatch;
1486
1567
  exports.map = map;
1487
1568
  exports.merge = merge;
1488
1569
  exports.multiple = multiple;
package/dist/parser.d.cts CHANGED
@@ -648,6 +648,88 @@ declare function or<TA, TB, TC, TD, TE, TF, TG, TH, TI, TStateA, TStateB, TState
648
648
  * in order, returning the result of the first successful parser.
649
649
  */
650
650
  declare function or<TA, TB, TC, TD, TE, TF, TG, TH, TI, TJ, TStateA, TStateB, TStateC, TStateD, TStateE, TStateF, TStateG, TStateH, TStateI, TStateJ>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>, e: Parser<TE, TStateE>, f: Parser<TF, TStateF>, g: Parser<TG, TStateG>, h: Parser<TH, TStateH>, i: Parser<TI, TStateI>, j: Parser<TJ, TStateJ>): Parser<TA | TB | TC | TD | TE | TF | TG | TH | TI | TJ, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>] | [4, ParserResult<TStateE>] | [5, ParserResult<TStateF>] | [6, ParserResult<TStateG>] | [7, ParserResult<TStateH>] | [8, ParserResult<TStateI>] | [9, ParserResult<TStateJ>]>;
651
+ /**
652
+ * Creates a parser that combines two mutually exclusive parsers into one,
653
+ * selecting the parser that consumes the most tokens.
654
+ * The resulting parser will try both parsers and return the result
655
+ * of the parser that consumed more input tokens.
656
+ * @template TA The type of the value returned by the first parser.
657
+ * @template TB The type of the value returned by the second parser.
658
+ * @template TStateA The type of the state used by the first parser.
659
+ * @template TStateB The type of the state used by the second parser.
660
+ * @param a The first {@link Parser} to try.
661
+ * @param b The second {@link Parser} to try.
662
+ * @returns A {@link Parser} that tries to parse using both parsers
663
+ * and returns the result of the parser that consumed more tokens.
664
+ * @since 0.3.0
665
+ */
666
+ declare function longestMatch<TA, TB, TStateA, TStateB>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>): Parser<TA | TB, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>]>;
667
+ /**
668
+ * Creates a parser that combines three mutually exclusive parsers into one,
669
+ * selecting the parser that consumes the most tokens.
670
+ * The resulting parser will try all parsers and return the result
671
+ * of the parser that consumed the most input tokens.
672
+ * @template TA The type of the value returned by the first parser.
673
+ * @template TB The type of the value returned by the second parser.
674
+ * @template TC The type of the value returned by the third parser.
675
+ * @template TStateA The type of the state used by the first parser.
676
+ * @template TStateB The type of the state used by the second parser.
677
+ * @template TStateC The type of the state used by the third parser.
678
+ * @param a The first {@link Parser} to try.
679
+ * @param b The second {@link Parser} to try.
680
+ * @param c The third {@link Parser} to try.
681
+ * @returns A {@link Parser} that tries to parse using all parsers
682
+ * and returns the result of the parser that consumed the most tokens.
683
+ * @since 0.3.0
684
+ */
685
+ declare function longestMatch<TA, TB, TC, TStateA, TStateB, TStateC>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>): Parser<TA | TB | TC, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>]>;
686
+ /**
687
+ * Creates a parser that combines four mutually exclusive parsers into one,
688
+ * selecting the parser that consumes the most tokens.
689
+ * The resulting parser will try all parsers and return the result
690
+ * of the parser that consumed the most input tokens.
691
+ * @template TA The type of the value returned by the first parser.
692
+ * @template TB The type of the value returned by the second parser.
693
+ * @template TC The type of the value returned by the third parser.
694
+ * @template TD The type of the value returned by the fourth parser.
695
+ * @template TStateA The type of the state used by the first parser.
696
+ * @template TStateB The type of the state used by the second parser.
697
+ * @template TStateC The type of the state used by the third parser.
698
+ * @template TStateD The type of the state used by the fourth parser.
699
+ * @param a The first {@link Parser} to try.
700
+ * @param b The second {@link Parser} to try.
701
+ * @param c The third {@link Parser} to try.
702
+ * @param d The fourth {@link Parser} to try.
703
+ * @returns A {@link Parser} that tries to parse using all parsers
704
+ * and returns the result of the parser that consumed the most tokens.
705
+ * @since 0.3.0
706
+ */
707
+ declare function longestMatch<TA, TB, TC, TD, TStateA, TStateB, TStateC, TStateD>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>): Parser<TA | TB | TC | TD, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>]>;
708
+ /**
709
+ * Creates a parser that combines five mutually exclusive parsers into one,
710
+ * selecting the parser that consumes the most tokens.
711
+ * The resulting parser will try all parsers and return the result
712
+ * of the parser that consumed the most input tokens.
713
+ * @template TA The type of the value returned by the first parser.
714
+ * @template TB The type of the value returned by the second parser.
715
+ * @template TC The type of the value returned by the third parser.
716
+ * @template TD The type of the value returned by the fourth parser.
717
+ * @template TE The type of the value returned by the fifth parser.
718
+ * @template TStateA The type of the state used by the first parser.
719
+ * @template TStateB The type of the state used by the second parser.
720
+ * @template TStateC The type of the state used by the third parser.
721
+ * @template TStateD The type of the state used by the fourth parser.
722
+ * @template TStateE The type of the state used by the fifth parser.
723
+ * @param a The first {@link Parser} to try.
724
+ * @param b The second {@link Parser} to try.
725
+ * @param c The third {@link Parser} to try.
726
+ * @param d The fourth {@link Parser} to try.
727
+ * @param e The fifth {@link Parser} to try.
728
+ * @returns A {@link Parser} that tries to parse using all parsers
729
+ * and returns the result of the parser that consumed the most tokens.
730
+ * @since 0.3.0
731
+ */
732
+ declare function longestMatch<TA, TB, TC, TD, TE, TStateA, TStateB, TStateC, TStateD, TStateE>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>, e: Parser<TE, TStateE>): Parser<TA | TB | TC | TD | TE, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>] | [4, ParserResult<TStateE>]>;
651
733
  /**
652
734
  * Helper type to check if all members of a union are object-like.
653
735
  * This allows merge() to work with parsers like withDefault() that produce union types.
@@ -936,4 +1018,4 @@ declare function parse<T>(parser: Parser<T, unknown>, args: readonly string[]):
936
1018
  */
937
1019
  declare function getDocPage(parser: Parser<unknown, unknown>, args?: readonly string[]): DocPage | undefined;
938
1020
  //#endregion
939
- export { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
1021
+ export { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
package/dist/parser.d.ts CHANGED
@@ -648,6 +648,88 @@ declare function or<TA, TB, TC, TD, TE, TF, TG, TH, TI, TStateA, TStateB, TState
648
648
  * in order, returning the result of the first successful parser.
649
649
  */
650
650
  declare function or<TA, TB, TC, TD, TE, TF, TG, TH, TI, TJ, TStateA, TStateB, TStateC, TStateD, TStateE, TStateF, TStateG, TStateH, TStateI, TStateJ>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>, e: Parser<TE, TStateE>, f: Parser<TF, TStateF>, g: Parser<TG, TStateG>, h: Parser<TH, TStateH>, i: Parser<TI, TStateI>, j: Parser<TJ, TStateJ>): Parser<TA | TB | TC | TD | TE | TF | TG | TH | TI | TJ, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>] | [4, ParserResult<TStateE>] | [5, ParserResult<TStateF>] | [6, ParserResult<TStateG>] | [7, ParserResult<TStateH>] | [8, ParserResult<TStateI>] | [9, ParserResult<TStateJ>]>;
651
+ /**
652
+ * Creates a parser that combines two mutually exclusive parsers into one,
653
+ * selecting the parser that consumes the most tokens.
654
+ * The resulting parser will try both parsers and return the result
655
+ * of the parser that consumed more input tokens.
656
+ * @template TA The type of the value returned by the first parser.
657
+ * @template TB The type of the value returned by the second parser.
658
+ * @template TStateA The type of the state used by the first parser.
659
+ * @template TStateB The type of the state used by the second parser.
660
+ * @param a The first {@link Parser} to try.
661
+ * @param b The second {@link Parser} to try.
662
+ * @returns A {@link Parser} that tries to parse using both parsers
663
+ * and returns the result of the parser that consumed more tokens.
664
+ * @since 0.3.0
665
+ */
666
+ declare function longestMatch<TA, TB, TStateA, TStateB>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>): Parser<TA | TB, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>]>;
667
+ /**
668
+ * Creates a parser that combines three mutually exclusive parsers into one,
669
+ * selecting the parser that consumes the most tokens.
670
+ * The resulting parser will try all parsers and return the result
671
+ * of the parser that consumed the most input tokens.
672
+ * @template TA The type of the value returned by the first parser.
673
+ * @template TB The type of the value returned by the second parser.
674
+ * @template TC The type of the value returned by the third parser.
675
+ * @template TStateA The type of the state used by the first parser.
676
+ * @template TStateB The type of the state used by the second parser.
677
+ * @template TStateC The type of the state used by the third parser.
678
+ * @param a The first {@link Parser} to try.
679
+ * @param b The second {@link Parser} to try.
680
+ * @param c The third {@link Parser} to try.
681
+ * @returns A {@link Parser} that tries to parse using all parsers
682
+ * and returns the result of the parser that consumed the most tokens.
683
+ * @since 0.3.0
684
+ */
685
+ declare function longestMatch<TA, TB, TC, TStateA, TStateB, TStateC>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>): Parser<TA | TB | TC, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>]>;
686
+ /**
687
+ * Creates a parser that combines four mutually exclusive parsers into one,
688
+ * selecting the parser that consumes the most tokens.
689
+ * The resulting parser will try all parsers and return the result
690
+ * of the parser that consumed the most input tokens.
691
+ * @template TA The type of the value returned by the first parser.
692
+ * @template TB The type of the value returned by the second parser.
693
+ * @template TC The type of the value returned by the third parser.
694
+ * @template TD The type of the value returned by the fourth parser.
695
+ * @template TStateA The type of the state used by the first parser.
696
+ * @template TStateB The type of the state used by the second parser.
697
+ * @template TStateC The type of the state used by the third parser.
698
+ * @template TStateD The type of the state used by the fourth parser.
699
+ * @param a The first {@link Parser} to try.
700
+ * @param b The second {@link Parser} to try.
701
+ * @param c The third {@link Parser} to try.
702
+ * @param d The fourth {@link Parser} to try.
703
+ * @returns A {@link Parser} that tries to parse using all parsers
704
+ * and returns the result of the parser that consumed the most tokens.
705
+ * @since 0.3.0
706
+ */
707
+ declare function longestMatch<TA, TB, TC, TD, TStateA, TStateB, TStateC, TStateD>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>): Parser<TA | TB | TC | TD, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>]>;
708
+ /**
709
+ * Creates a parser that combines five mutually exclusive parsers into one,
710
+ * selecting the parser that consumes the most tokens.
711
+ * The resulting parser will try all parsers and return the result
712
+ * of the parser that consumed the most input tokens.
713
+ * @template TA The type of the value returned by the first parser.
714
+ * @template TB The type of the value returned by the second parser.
715
+ * @template TC The type of the value returned by the third parser.
716
+ * @template TD The type of the value returned by the fourth parser.
717
+ * @template TE The type of the value returned by the fifth parser.
718
+ * @template TStateA The type of the state used by the first parser.
719
+ * @template TStateB The type of the state used by the second parser.
720
+ * @template TStateC The type of the state used by the third parser.
721
+ * @template TStateD The type of the state used by the fourth parser.
722
+ * @template TStateE The type of the state used by the fifth parser.
723
+ * @param a The first {@link Parser} to try.
724
+ * @param b The second {@link Parser} to try.
725
+ * @param c The third {@link Parser} to try.
726
+ * @param d The fourth {@link Parser} to try.
727
+ * @param e The fifth {@link Parser} to try.
728
+ * @returns A {@link Parser} that tries to parse using all parsers
729
+ * and returns the result of the parser that consumed the most tokens.
730
+ * @since 0.3.0
731
+ */
732
+ declare function longestMatch<TA, TB, TC, TD, TE, TStateA, TStateB, TStateC, TStateD, TStateE>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>, e: Parser<TE, TStateE>): Parser<TA | TB | TC | TD | TE, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>] | [4, ParserResult<TStateE>]>;
651
733
  /**
652
734
  * Helper type to check if all members of a union are object-like.
653
735
  * This allows merge() to work with parsers like withDefault() that produce union types.
@@ -936,4 +1018,4 @@ declare function parse<T>(parser: Parser<T, unknown>, args: readonly string[]):
936
1018
  */
937
1019
  declare function getDocPage(parser: Parser<unknown, unknown>, args?: readonly string[]): DocPage | undefined;
938
1020
  //#endregion
939
- export { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
1021
+ export { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
package/dist/parser.js CHANGED
@@ -1031,6 +1031,86 @@ function or(...parsers) {
1031
1031
  }
1032
1032
  };
1033
1033
  }
1034
+ function longestMatch(...parsers) {
1035
+ return {
1036
+ $valueType: [],
1037
+ $stateType: [],
1038
+ priority: Math.max(...parsers.map((p) => p.priority)),
1039
+ usage: [{
1040
+ type: "exclusive",
1041
+ terms: parsers.map((p) => p.usage)
1042
+ }],
1043
+ initialState: void 0,
1044
+ complete(state) {
1045
+ if (state == null) return {
1046
+ success: false,
1047
+ error: message`No parser matched.`
1048
+ };
1049
+ const [i, result] = state;
1050
+ if (result.success) return parsers[i].complete(result.next.state);
1051
+ return {
1052
+ success: false,
1053
+ error: result.error
1054
+ };
1055
+ },
1056
+ parse(context) {
1057
+ let bestMatch = null;
1058
+ let error = {
1059
+ consumed: 0,
1060
+ error: context.buffer.length < 1 ? message`No parser matched.` : message`Unexpected option or subcommand: ${optionName(context.buffer[0])}.`
1061
+ };
1062
+ for (let i = 0; i < parsers.length; i++) {
1063
+ const parser = parsers[i];
1064
+ const result = parser.parse({
1065
+ ...context,
1066
+ state: context.state == null || context.state[0] !== i || !context.state[1].success ? parser.initialState : context.state[1].next.state
1067
+ });
1068
+ if (result.success) {
1069
+ const consumed = context.buffer.length - result.next.buffer.length;
1070
+ if (bestMatch === null || consumed > bestMatch.consumed) bestMatch = {
1071
+ index: i,
1072
+ result,
1073
+ consumed
1074
+ };
1075
+ } else if (error.consumed < result.consumed) error = result;
1076
+ }
1077
+ if (bestMatch && bestMatch.result.success) return {
1078
+ success: true,
1079
+ next: {
1080
+ ...context,
1081
+ buffer: bestMatch.result.next.buffer,
1082
+ optionsTerminated: bestMatch.result.next.optionsTerminated,
1083
+ state: [bestMatch.index, bestMatch.result]
1084
+ },
1085
+ consumed: bestMatch.result.consumed
1086
+ };
1087
+ return {
1088
+ ...error,
1089
+ success: false
1090
+ };
1091
+ },
1092
+ getDocFragments(state, _defaultValue) {
1093
+ let description;
1094
+ let fragments;
1095
+ if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
1096
+ else {
1097
+ const [i, result] = state.state;
1098
+ if (result.success) {
1099
+ const docResult = parsers[i].getDocFragments({
1100
+ kind: "available",
1101
+ state: result.next.state
1102
+ });
1103
+ description = docResult.description;
1104
+ fragments = docResult.fragments;
1105
+ } else fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
1106
+ }
1107
+ return {
1108
+ description,
1109
+ fragments
1110
+ };
1111
+ }
1112
+ };
1113
+ }
1034
1114
  function merge(...parsers) {
1035
1115
  parsers = parsers.toSorted((a, b) => b.priority - a.priority);
1036
1116
  const initialState = {};
@@ -1477,4 +1557,4 @@ function getDocPage(parser, args = []) {
1477
1557
  }
1478
1558
 
1479
1559
  //#endregion
1480
- export { argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
1560
+ export { argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.3.0-dev.40+7b4a92a1",
3
+ "version": "0.3.0-dev.42+34a9d922",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",