| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856 |
- // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- // Exceptions. See /LICENSE for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #ifndef CARBON_COMMON_COMMAND_LINE_H_
- #define CARBON_COMMON_COMMAND_LINE_H_
- #include <initializer_list>
- #include <memory>
- #include <tuple>
- #include <type_traits>
- #include <utility>
- #include "common/check.h"
- #include "common/ostream.h"
- #include "llvm/ADT/ArrayRef.h"
- #include "llvm/ADT/DenseMap.h"
- #include "llvm/ADT/DenseSet.h"
- #include "llvm/ADT/PointerIntPair.h"
- #include "llvm/ADT/STLForwardCompat.h"
- #include "llvm/ADT/Sequence.h"
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/ADT/StringExtras.h"
- #include "llvm/ADT/StringRef.h"
- #include "llvm/Support/Allocator.h"
- // # Command-line argument parsing library.
- //
- // This is a collection of tools to describe both simple and reasonably complex
- // command line interfaces, and parse arguments based on those descriptions. It
- // optimizes for command line tools that will parse arguments exactly once per
- // execution, and specifically tools that make heavy use of subcommand-style
- // command line interfaces.
- //
- // ## Terminology used by this library
- //
- // _Argument_ or _arg_: One of the parsed components of the command line.
- //
- // _Option_: A _named_ argument starting with `--` to configure some aspect of
- // the tool. These often have both long spellings that start with `--` and
- // short, single character spellings that start with `-` and can be bundled with
- // other single character options.
- //
- // _Flag_: A boolean or binary option. These can only be enabled or disabled.
- //
- // _Positional argument_: An argument that is not named but is identified based
- // on the order in which it is encountered in the command line, with options
- // removed. Only a leaf command can contain positional arguments.
- //
- // _Value_: The value parameter provided to an argument. For options, this is
- // provided after an `=` and the option name. For positional arguments, the
- // value is the only thing provided. The string of the value is parsed according
- // to the kind of argument with fairly simple rules. See the argument builders
- // for more details.
- //
- // _Command_: The container of options, subcommands, and positional arguments to
- // parse and an action to take when successful.
- //
- // _Leaf command_: A command that doesn't contain subcommands. This is the only
- // kind of command that can contain positional arguments.
- //
- // _Subcommand_: A command nested within another command and identified by a
- // specific name that ends the parsing of arguments based on the parent and
- // switches to parse based on the specific subcommand's options and positional
- // arguments. A command with subcommands cannot parse positional arguments.
- //
- // _Action_: An open-ended callback, typically reflecting a specific subcommand
- // being parsed. Can either directly perform the operation or simply mark which
- // operation was selected.
- //
- // _Meta action_: An action fully handled by argument parsing such as displaying
- // help, version information, or completion.
- //
- // Example command to illustrate the different components:
- //
- // git --no-pager clone --shared --filter=blob:none my-repo my-directory
- //
- // This command breaks down as:
- // - `git`: The top-level command.
- // - `--no-pager`: A negated flag on the top-level command (`git`).
- // - `clone`: A subcommand.
- // - `--shared`: A positive flag for the subcommand (`clone`).
- // - `--filter=blob:none`: An option named `filter` with a value `blob:none`.
- // - `my-repo`: The first positional argument to the subcommand.
- // - `my-directory`: the second positional argument to the subcommand.
- //
- // **Note:** while the example uses a `git` command to be relatively familiar
- // and well documented, this library does not support the same flag syntaxes as
- // `git` or use anything similar for its subcommand dispatch. This example is
- // just to help clarify the terminology used, and carefully chosen to only use a
- // syntax that overlaps with this library's parsed syntax.
- //
- // ## Options and flags
- //
- // The option syntax and behavior supported by this library is designed to be
- // strict and relatively simple while still supporting a wide range of expected
- // use cases:
- //
- // - All options must have a unique long name that is accessed with a `--`
- // prefix. The name must consist of characters in the set [-a-zA-Z0-9], and it
- // must not start with a `-` or `no-`.
- //
- // - Values are always attached using an `=` after the name. Only a few simple
- // value formats are supported:
- // - Arbitrary strings
- // - Integers as parsed by `llvm::StringRef` and whose value fits in an
- // `int`.
- // - One of a fixed set of strings
- //
- // - Options may be parsed multiple times, and the behavior can be configured:
- // - Each time, they can set a new value, overwriting any previous.
- // - They can append the value to a container.
- // - TODO: They can increment a count.
- //
- // - Options may have a default value that will be synthesized even if they do
- // not occur in the parsed command line.
- //
- // - Flags (boolean options) have some special rules.
- // - They may be spelled normally, and default to setting that flag to `true`.
- // - They may also accept a value of either exactly `true` or `false` and that
- // is the value.
- // - They may be spelled with a `no-` prefix, such as `--no-verbose`, and that
- // is exactly equivalent to `--verbose=false`.
- // - For flags with a default `true` value, they are rendered in help using
- // the `no-` prefix.
- //
- // - Options may additionally have a short name of a single character [a-zA-Z].
- // - There is no distinction between the behavior of long and short names.
- // - The short name can only specify the positive or `true` value for flags.
- // There is no negative form of short names.
- // - Short names are parsed after a single `-`, such as `-v`.
- // - Any number of short names for boolean flags or options with default
- // values may be grouped after `-`, such as `-xyz`: this is three options,
- // `x`, `y`, and `z`.
- // - Short options may include a value after an `=`, but not when grouped with
- // other short options.
- //
- // - Options are parsed from any argument until either a subcommand switches to
- // that subcommand's options or a `--` argument ends all option parsing to
- // allow arguments that would be ambiguous with the option syntax.
- //
- // ## Subcommands
- //
- // Subcommands can be nested to any depth, and each subcommand gets its own
- // option space.
- //
- // ## Positional arguments
- //
- // Leaf commands (and subcommands) can accept positional arguments. These work
- // similar to options but consist *only* of the value. They have names, but the
- // name is only used in documentation and error messages. Except for the special
- // case of the exact argument `-`, positional argument values cannot start with
- // a `-` character until after option parsing is ended with a `--` argument.
- //
- // Singular positional arguments store the single value in that position.
- // Appending positional arguments append all of the values in sequence.
- //
- // Multiple positional arguments to a single command are parsed in sequence. For
- // appending positional arguments, the sequence of values appended is ended with
- // a `--` argument. For example, a command that takes two sequences of
- // positional arguments might look like:
- //
- // my-diff-tool a.txt b.txt c.txt -- new-a.txt new-b.txt new-c.txt
- //
- // The `--` used in this way *both* ends option parsing and the positional
- // argument sequence. Note that if there are no positional arguments prior to
- // the first `--`, then it will just end option parsing. To pass an empty
- // sequence of positional arguments two `--` arguments would be required. Once
- // option parsing is ended, even a single positional argument can be skipped
- // using a `--` argument without a positional argument.
- //
- // ## Help text blocks and rendering
- //
- // At many points in this library, a block of text is specified as a string.
- // These should be written using multi-line raw string literals that start on
- // their own line such as:
- //
- // ```cpp
- // ...
- // .help = R"""(
- // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
- // tempor incididunt ut labore et dolore magna aliqua.
- // )""",
- // ```
- //
- // The code using these blocks will remove leading and trailing newlines. TODO:
- // It should also reflow all of the text in a way that tries to be roughly
- // similar to how Markdown would be reflowed when rendered:
- //
- // - Blank lines will be preserved.
- // - (TODO) A best effort to detect lists and other common Markdown constructs
- // and preserve the newlines between those.
- // - (TODO) Fenced regions will be preserved exactly.
- //
- // The remaining blocks will have all newlines removed when there is no column
- // width information on the output stream, or will be re-flowed to the output
- // stream's column width when available.
- //
- // ## The `Args` class
- //
- // The `Args` class is not a meaningful type, it serves two purposes: to give a
- // structured name prefix to the inner types, and to allow marking many
- // components `private` and using `friend` to grant access to implementation
- // details.
- //
- // ## Roadmap / TODOs / planned future work
- //
- // - Detect column width when the stream is a terminal and re-flow text.
- // - Implement short help display mode (and enable that by default).
- // - Add help text to one-of values and render it when present.
- // - Add formatting when the stream supports it (colors, bold, etc).
- // - Improve error messages when parsing fails.
- // - Reliably display the most appropriate usage string.
- // - Suggest typo corrections?
- // - Print snippet or otherwise render the failure better?
- // - Add support for responding to shell autocomplete queries.
- // - Finish adding support for setting and printing version information.
- // - Add short option counting support (`-vvv` -> `--verbose=3`).
- //
- namespace Carbon::CommandLine {
- // Forward declare some implementation detail classes and classes that are
- // friended.
- struct Arg;
- struct Command;
- class MetaPrinter;
- class Parser;
- class CommandBuilder;
- // The result of parsing arguments can be a parse error, a successfully parsed
- // command line, or a meta-success due to triggering a meta-action during the
- // parse such as rendering help text.
- enum class ParseResult {
- // Signifies an error parsing arguments. It will have been diagnosed using
- // the streams provided to the parser, and no useful parsed arguments are
- // available.
- Error,
- // Signifies that program arguments were successfully parsed and can be
- // used.
- Success,
- // Signifies a successful meta operation such as displaying help text
- // was performed. No parsed arguments are available, and the side-effects
- // have been directly provided via the streams provided to the parser.
- MetaSuccess,
- };
- auto operator<<(llvm::raw_ostream& output, ParseResult result)
- -> llvm::raw_ostream&;
- // Actions are stored in data structures so we use an owning closure to model
- // them.
- using ActionT = std::function<void()>;
- // The core argument info used to render help and other descriptive
- // information. This is used for both options and positional arguments.
- // Commands use an extended version below.
- struct ArgInfo {
- // The name of the argument. For options, this is the long name parsed after
- // `--`. Option names must also be unique. Conventionally, these are spelled
- // in lower case with a single dash separating words as that tends to be
- // especially easy to type and reasonably easy to read.
- //
- // For positional arguments this is only used for help text, and is often
- // spelled with `ALL-CAPS` to make it clear that it is a placeholder name
- // and does not appear in the actual command line.
- llvm::StringRef name;
- // Optional short name for options. This must consist of a single character
- // in the set [a-zA-Z].
- llvm::StringRef short_name = "";
- // For options, it is sometimes useful to render a distinct value
- // placeholder name to help clarify what the value should contain. These are
- // often in `ALL-CAPS` to make it clear they are placeholders. For example,
- // an `output` option might set this to `FILE` so it renders as
- // `--output=FILE`.
- llvm::StringRef value_name = "";
- // A long-form help string that will be rendered for this argument in
- // command help strings. It supports multiple lines and is rendered as a
- // text block described in the `Args` top-level comment.
- llvm::StringRef help = "";
- // A short help string for the argument. This should be as short as
- // possible, and always fit easily on a single line. Ideally, it can fit in
- // a narrow column of a single line and is in the range of 40 columns wide.
- //
- // If not provided, the first paragraph (up to a blank line) from `help`
- // will be used.
- llvm::StringRef help_short = "";
- };
- // The kinds of arguments that can be parsed.
- enum class ArgKind {
- Invalid,
- Flag,
- Integer,
- String,
- OneOf,
- MetaActionOnly,
- };
- auto operator<<(llvm::raw_ostream& output, ArgKind kind) -> llvm::raw_ostream&;
- // A builder used to configure an argument for parsing.
- class ArgBuilder {
- public:
- // When marked as required, if an argument is not provided explicitly in the
- // command line the parse will produce an error.
- void Required(bool is_required);
- // An argument can be hidden from the help output.
- void HelpHidden(bool is_help_hidden);
- // Sets a meta-action to run when this argument is parsed. This is used to
- // set up arguments like `--help` or `--version` that can be entirely
- // handled during parsing and rather than produce parsed information about
- // the command line, override that for some custom behavior.
- template <typename T>
- void MetaAction(T action);
- protected:
- friend CommandBuilder;
- explicit ArgBuilder(Arg& arg);
- Arg& arg_;
- };
- // Customized argument builder when the value is a boolean flag.
- class FlagBuilder : public ArgBuilder {
- public:
- // Flags can be defaulted to true. However, flags always have *some*
- // default, this merely customizes which value is default. If uncustomized,
- // the default of a flag is false.
- void Default(bool flag_value);
- // Configures the argument to store a parsed value in the provided storage.
- //
- // This must be called on the builder.
- void Set(bool* flag);
- private:
- using ArgBuilder::ArgBuilder;
- };
- // Customized argument builder when the value is an integer.
- class IntegerArgBuilder : public ArgBuilder {
- public:
- // Sets a default for the argument with the provided value. There is no
- // default for integer arguments unless this is called.
- //
- // For arguments that are `Set` below, this value will be stored even if the
- // argument is never explicitly provided. For arguments that are `Append`ed
- // below, this value will be used whenever the argument occurs without an
- // explicit value, but unless the argument is parsed nothing will be
- // appended.
- void Default(int integer_value);
- // Configures the argument to store a parsed value in the provided storage.
- // Each time the argument is parsed, it will write a new value to this
- // storage.
- //
- // Exactly one of this method or `Append` below must be configured for the
- // argument.
- void Set(int* integer);
- // Configures the argument to append a parsed value to the provided
- // container. Each time the argument is parsed, a new value will be
- // appended.
- //
- // Exactly one of this method or `Set` above must be configured for the
- // argument.
- void Append(llvm::SmallVectorImpl<int>* sequence);
- private:
- using ArgBuilder::ArgBuilder;
- };
- // Customized argument builder when the value is a string.
- class StringArgBuilder : public ArgBuilder {
- public:
- // Sets a default for the argument with the provided value. There is no
- // default for string arguments unless this is called.
- //
- // For arguments that are `Set` below, this value will be stored even if the
- // argument is never explicitly provided. For arguments that are `Append`ed
- // below, this value will be used whenever the argument occurs without an
- // explicit value, but unless the argument is parsed nothing will be
- // appended.
- void Default(llvm::StringRef string_value);
- // Configures the argument to store a parsed value in the provided storage.
- // Each time the argument is parsed, it will write a new value to this
- // storage.
- //
- // Exactly one of this method or `Append` below must be configured for the
- // argument.
- void Set(llvm::StringRef* string);
- // Configures the argument to append a parsed value to the provided
- // container. Each time the argument is parsed, a new value will be
- // appended.
- //
- // Exactly one of this method or `Set` above must be configured for the
- // argument.
- void Append(llvm::SmallVectorImpl<llvm::StringRef>* sequence);
- private:
- using ArgBuilder::ArgBuilder;
- };
- // Customized argument builder when the value is required to be one of a fixed
- // set of possible strings, and each one maps to a specific value of some
- // type, often an enumerator.
- class OneOfArgBuilder : public ArgBuilder {
- public:
- // A tiny helper / builder type for describing one of the possible values
- // that a particular argument accepts.
- //
- // Beyond carrying the string, type, and value for this candidate, it also
- // allows marking the candidate as the default.
- template <typename T>
- class OneOfValueT {
- public:
- // Configure whether a candidate is the default. If not called, it is not
- // the default. This can only be used when setting, not when appending.
- auto Default(bool is_default) && -> OneOfValueT;
- private:
- friend OneOfArgBuilder;
- explicit OneOfValueT(llvm::StringRef str, T value);
- llvm::StringRef str;
- T value;
- bool is_default = false;
- };
- // A helper function to create an object that models one of the candidate
- // values for this argument. It takes the string used to select this value
- // on the command line, deduces the type of the value, and accepts the value
- // itself that should be used when this candidate is parsed.
- template <typename T>
- static auto OneOfValue(llvm::StringRef string, T value) -> OneOfValueT<T>;
- // Configures the argument to store a parsed value in the provided storage.
- // Each time the argument is parsed, it will write a new value to this
- // storage.
- //
- // Exactly one of this method or `AppendOneOf` below must be configured for
- // the argument.
- //
- // For one-of arguments, this method also provides an array of possible
- // values that may be used:
- //
- // ```cpp
- // arg_b.SetOneOf(
- // {
- // arg_b.OneOfValue("x", 1),
- // arg_b.OneOfValue("y", 2),
- // arg_b.OneOfValue("z", 3).Default(true),
- // },
- // &value);
- // ```
- //
- // The only value strings that will be parsed are those described in the
- // array, and the value stored in the storage for a particular parsed value
- // string is the one associated with it. There must be a single homogeneous
- // type for the all of the candidate values and that type must be storable
- // into the result storage pointee type. At most one of the array can also
- // be marked as a default value, which will make specifying a value
- // optional, and also will cause that value to be stored into the result
- // even if the argument is not parsed explicitly.
- template <typename T, typename U, size_t N>
- void SetOneOf(const OneOfValueT<U> (&values)[N], T* result);
- // Configures the argument to append a parsed value to the provided
- // container. Each time the argument is parsed, a new value will be
- // appended.
- //
- // Exactly one of this method or `SetOneOf` above must be configured for the
- // argument.
- //
- // Similar to `SetOneOf`, this must also describe the candidate value
- // strings that can be parsed and the consequent values that are used for
- // those strings:
- //
- // ```cpp
- // arg_b.AppendOneOf(
- // {
- // arg_b.OneOfValue("x", 1),
- // arg_b.OneOfValue("y", 2),
- // arg_b.OneOfValue("z", 3),
- // },
- // &values);
- // ```
- //
- // Instead of storing, these values are appended.
- //
- // However, appending one-of arguments cannot use a default. The values must
- // always be explicitly parsed.
- template <typename T, typename U, size_t N>
- void AppendOneOf(const OneOfValueT<U> (&values)[N],
- llvm::SmallVectorImpl<T>* sequence);
- private:
- using ArgBuilder::ArgBuilder;
- template <typename U, size_t N, typename MatchT, size_t... Indices>
- void OneOfImpl(const OneOfValueT<U> (&input_values)[N], MatchT match,
- std::index_sequence<Indices...> /*indices*/);
- };
- // The extended info for a command, including for a subcommand.
- //
- // This provides the primary descriptive information for commands used by help
- // and other diagnostic messages. For subcommands, it also provides the name
- // used to access the subcommand.
- struct CommandInfo {
- // The name of the command. For subcommands, this is also the argument
- // spelling that accesses this subcommand. It must consist of characters in
- // the set [-a-zA-Z0-9], and must not start with a `-`.
- llvm::StringRef name;
- // An optional version string that will be rendered as part of version meta
- // action by the library. While this library does not impose a machine
- // parsable structure, users will expect this to be extractable and parsable
- // in practice.
- //
- // Subcommands with an empty version will inherit the first non-empty parent
- // version.
- //
- // Whether a (possibly inherited) version string is non-empty determines
- // whether this library provides the version-printing meta actions via a
- // `--version` flag or, if there are subcommands, a `version` subcommand.
- llvm::StringRef version = "";
- // Optional build information to include when printing the version.
- llvm::StringRef build_info = "";
- // An optional long-form help string for the command.
- //
- // When accessing a command's dedicated help output, this will form the main
- // prose output at the beginning of the help message. When listing
- // subcommands, the subcommand's `help` string will be used to describe it
- // in the list.
- //
- // The help meta actions are available regardless of whether this is
- // provided in order to mechanically describe other aspects of the command.
- //
- // This field supports multiple lines and uses the text block handling
- // described in the top-level `Args` comment.
- llvm::StringRef help = "";
- // An optional short help string for the command.
- //
- // This should be very short, at most one line and ideally fitting within a
- // narrow column of a line. If left blank, the first paragraph of text (up
- // to the first blank line) in the `help` string will be used.
- llvm::StringRef help_short = "";
- // An optional custom block of text to describe the usage of the command.
- //
- // The usage should just be a series of lines with possible invocations of
- // the command summarized as briefly as possible. Ideally, each variation on
- // a single line. The goal is to show the *structure* of different command
- // invocations, not to be comprehensive.
- //
- // When omitted, the library will generate these based on the parsing logic.
- // The generated usage is expected to be suitable for the vast majority of
- // users, but can be customized in cases where necessary.
- llvm::StringRef usage = "";
- // An optional epilogue multi-line block of text appended to the help display
- // for this command. It is only used for this command's dedicated help, but
- // can contain extra, custom guidance that is especially useful to have at
- // the very end of the output.
- llvm::StringRef help_epilogue = "";
- };
- // Commands are classified based on the action they result in when run.
- //
- // Commands that require a subcommand have no action and are just a means to
- // reach the subcommand actions.
- //
- // Commands with _meta_ actions are also a separate kind from those with
- // normal actions.
- enum class CommandKind {
- Invalid,
- RequiresSubcommand,
- Action,
- MetaAction,
- };
- auto operator<<(llvm::raw_ostream& output, CommandKind kind)
- -> llvm::raw_ostream&;
- // Builder used to configure a command to parse.
- //
- // An instance of this is used to configure the top-level command, and a fresh
- // instance is provided for configuring each subcommand as well.
- class CommandBuilder {
- public:
- using Kind = CommandKind;
- void AddFlag(const ArgInfo& info,
- llvm::function_ref<void(FlagBuilder&)> build);
- void AddIntegerOption(const ArgInfo& info,
- llvm::function_ref<void(IntegerArgBuilder&)> build);
- void AddStringOption(const ArgInfo& info,
- llvm::function_ref<void(StringArgBuilder&)> build);
- void AddOneOfOption(const ArgInfo& info,
- llvm::function_ref<void(OneOfArgBuilder&)> build);
- void AddMetaActionOption(const ArgInfo& info,
- llvm::function_ref<void(ArgBuilder&)> build);
- void AddIntegerPositionalArg(
- const ArgInfo& info, llvm::function_ref<void(IntegerArgBuilder&)> build);
- void AddStringPositionalArg(
- const ArgInfo& info, llvm::function_ref<void(StringArgBuilder&)> build);
- void AddOneOfPositionalArg(const ArgInfo& info,
- llvm::function_ref<void(OneOfArgBuilder&)> build);
- void AddSubcommand(const CommandInfo& info,
- llvm::function_ref<void(CommandBuilder&)> build);
- // Subcommands can be hidden from the help listing of their parents with
- // this setting. Hiding a subcommand doesn't disable its own help, it just
- // removes it from the listing.
- void HelpHidden(bool is_help_hidden);
- // Exactly one of these three should be called to select and configure the
- // kind of the built command.
- void RequiresSubcommand();
- void Do(ActionT action);
- void Meta(ActionT meta_action);
- private:
- friend Parser;
- explicit CommandBuilder(Command& command, MetaPrinter& meta_printer);
- auto AddArgImpl(const ArgInfo& info, ArgKind kind) -> Arg&;
- void AddPositionalArgImpl(const ArgInfo& info, ArgKind kind,
- llvm::function_ref<void(Arg&)> build);
- void Finalize();
- Command& command_;
- MetaPrinter& meta_printer_;
- llvm::SmallDenseSet<llvm::StringRef> arg_names_;
- llvm::SmallDenseSet<llvm::StringRef> subcommand_names_;
- };
- // Builds a description of a command and then parses the provided arguments
- // for that command.
- //
- // This is the main entry point to both build up the description of the
- // command whose arguments are being parsed and to do the parsing. Everything
- // is done in a single invocation as the common case is to build a command
- // description, parse the arguments once, and then run with that
- // configuration.
- //
- // The `out` stream is treated like `stdout` would be for a Unix-style command
- // line tool, and `errors` like `stderr`: any errors or diagnostic information
- // are printed to `errors`, but meta-actions like printing a command's help go
- // to `out`.
- auto Parse(llvm::ArrayRef<llvm::StringRef> unparsed_args,
- llvm::raw_ostream& out, llvm::raw_ostream& errors,
- const CommandInfo& command_info,
- llvm::function_ref<void(CommandBuilder&)> build) -> ParseResult;
- // Implementation details only below.
- // The internal representation of a parsable argument description.
- struct Arg {
- using Kind = ArgKind;
- using ValueActionT =
- std::function<bool(const Arg& arg, llvm::StringRef value_string)>;
- using DefaultActionT = std::function<void(const Arg& arg)>;
- explicit Arg(const ArgInfo& info);
- ~Arg();
- ArgInfo info;
- Kind kind = Kind::Invalid;
- bool has_default = false;
- bool is_required = false;
- bool is_append = false;
- bool is_help_hidden = false;
- // Meta action storage, only populated if this argument causes a meta action.
- ActionT meta_action;
- // Storage options depending on the kind.
- union {
- // Singular argument storage pointers.
- bool* flag_storage;
- int* integer_storage;
- llvm::StringRef* string_storage;
- // Appending argument storage pointers.
- llvm::SmallVectorImpl<int>* integer_sequence;
- llvm::SmallVectorImpl<llvm::StringRef>* string_sequence;
- // One-of information.
- struct {
- llvm::OwningArrayRef<llvm::StringRef> value_strings;
- ValueActionT value_action;
- };
- };
- // Default values depending on the kind.
- union {
- bool default_flag;
- int default_integer;
- llvm::StringRef default_string;
- struct {
- DefaultActionT default_action;
- int default_value_index;
- };
- };
- };
- // The internal representation of a parsable command description, including its
- // options, positional arguments, and subcommands.
- struct Command {
- using Kind = CommandBuilder::Kind;
- explicit Command(const CommandInfo& info, Command* parent = nullptr);
- CommandInfo info;
- Command* parent;
- ActionT action;
- Kind kind = Kind::Invalid;
- bool is_help_hidden = false;
- llvm::SmallVector<std::unique_ptr<Arg>> options;
- llvm::SmallVector<std::unique_ptr<Arg>> positional_args;
- llvm::SmallVector<std::unique_ptr<Command>> subcommands;
- };
- template <typename T>
- void ArgBuilder::MetaAction(T action) {
- CARBON_CHECK(!arg_.meta_action) << "Cannot set a meta action twice!";
- arg_.meta_action = std::move(action);
- }
- template <typename T>
- auto OneOfArgBuilder::OneOfValueT<T>::Default(
- bool is_default) && -> OneOfValueT {
- OneOfValueT result = std::move(*this);
- result.is_default = is_default;
- return result;
- }
- template <typename T>
- OneOfArgBuilder::OneOfValueT<T>::OneOfValueT(llvm::StringRef str, T value)
- : str(str), value(std::move(value)) {}
- template <typename T>
- auto OneOfArgBuilder::OneOfValue(llvm::StringRef str, T value)
- -> OneOfValueT<T> {
- return OneOfValueT<T>(str, value);
- }
- template <typename T, typename U, size_t N>
- void OneOfArgBuilder::SetOneOf(const OneOfValueT<U> (&values)[N], T* result) {
- static_assert(N > 0, "Must include at least one value.");
- arg_.is_append = false;
- OneOfImpl(
- values, [result](T value) { *result = value; },
- std::make_index_sequence<N>{});
- }
- template <typename T, typename U, size_t N>
- void OneOfArgBuilder::AppendOneOf(const OneOfValueT<U> (&values)[N],
- llvm::SmallVectorImpl<T>* sequence) {
- static_assert(N > 0, "Must include at least one value.");
- arg_.is_append = true;
- OneOfImpl(
- values, [sequence](T value) { sequence->push_back(value); },
- std::make_index_sequence<N>{});
- }
- // An implementation tool for the one-of value candidate handling. Delegating to
- // this allows us to deduce a pack of indices from the array of candidates, and
- // then use that variadic pack to operate on the array in the variadic space.
- // This includes packaging the components up separately into our storage
- // representation, as well as processing the array to find and register any
- // default.
- //
- // The representation is especially tricky because we want all of the actual
- // values and even the *type* of values to be erased. We do that by building
- // lambdas that do the type-aware operations and storing those into type-erased
- // function objects.
- template <typename U, size_t N, typename MatchT, size_t... Indices>
- void OneOfArgBuilder::OneOfImpl(const OneOfValueT<U> (&input_values)[N],
- MatchT match,
- std::index_sequence<Indices...> /*indices*/) {
- std::array<llvm::StringRef, N> value_strings = {input_values[Indices].str...};
- std::array<U, N> values = {input_values[Indices].value...};
- // Directly copy the value strings into a heap-allocated array in the
- // argument.
- new (&arg_.value_strings)
- llvm::OwningArrayRef<llvm::StringRef>(value_strings);
- // And build a type-erased action that maps a specific value string to a value
- // by index.
- new (&arg_.value_action) Arg::ValueActionT(
- [values, match](const Arg& arg, llvm::StringRef value_string) -> bool {
- for (int i : llvm::seq<int>(0, N)) {
- if (value_string == arg.value_strings[i]) {
- match(values[i]);
- return true;
- }
- }
- return false;
- });
- // Fold over all the input values to see if there is a default.
- if ((input_values[Indices].is_default || ...)) {
- CARBON_CHECK(!arg_.is_append) << "Can't append default.";
- CARBON_CHECK((input_values[Indices].is_default + ... + 0) == 1)
- << "Cannot default more than one value.";
- arg_.has_default = true;
- // First build a lambda that configures the default using an index. We'll
- // call this below, this lambda isn't the one that is stored.
- auto set_default = [&](size_t index, const auto& default_value) {
- // Now that we have the desired default index, build a lambda and store it
- // as the default action. This lambda is stored and so it captures the
- // necessary information explicitly and by value.
- new (&arg_.default_action)
- Arg::DefaultActionT([value = default_value.value,
- match](const Arg& /*arg*/) { match(value); });
- // Also store the index itself for use when printing help.
- arg_.default_value_index = index;
- };
- // Now we fold across the inputs and in the one case that is the default, we
- // call the lambda. This is just a somewhat awkward way to write a loop with
- // a condition in it over a pack.
- ((input_values[Indices].is_default
- ? set_default(Indices, input_values[Indices])
- : static_cast<void>(0)),
- ...);
- }
- }
- } // namespace Carbon::CommandLine
- #endif // CARBON_COMMON_COMMAND_LINE_H_
|