[shuffle] Move utils to new structure.

The name `core` was decided fit better with the base language for the
IR. This CL moves the `core/` folder to `utils/` and moves the files
into the correct subdirectories. The build targets and namespaces are
not updated as part of the move and will be fixed up in later CLs.

Bug: tint:1988
Change-Id: I1fc4414c86b28e1669af2d2d07340ecfdd9ba681
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/142361
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/utils/cli/cli.h b/src/tint/utils/cli/cli.h
new file mode 100644
index 0000000..1f14fb2
--- /dev/null
+++ b/src/tint/utils/cli/cli.h
@@ -0,0 +1,410 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_UTILS_CLI_CLI_H_
+#define SRC_TINT_UTILS_CLI_CLI_H_
+
+#include <deque>
+#include <optional>
+#include <string>
+#include <utility>
+
+#include "src/tint/utils/containers/vector.h"
+#include "src/tint/utils/macros/compiler.h"
+#include "src/tint/utils/memory/block_allocator.h"
+#include "src/tint/utils/result/result.h"
+#include "src/tint/utils/text/parse_num.h"
+#include "src/tint/utils/text/string.h"
+
+namespace tint::utils::cli {
+
+/// Alias is a fluent-constructor helper for Options
+struct Alias {
+    /// The alias to apply to an Option
+    std::string value;
+
+    /// @param option the option to apply the alias to
+    template <typename T>
+    void Apply(T& option) {
+        option.alias = value;
+    }
+};
+
+/// ShortName is a fluent-constructor helper for Options
+struct ShortName {
+    /// The short-name to apply to an Option
+    std::string value;
+
+    /// @param option the option to apply the short name to
+    template <typename T>
+    void Apply(T& option) {
+        option.short_name = value;
+    }
+};
+
+/// Parameter is a fluent-constructor helper for Options
+struct Parameter {
+    /// The parameter name to apply to an Option
+    std::string value;
+
+    /// @param option the option to apply the parameter name to
+    template <typename T>
+    void Apply(T& option) {
+        option.parameter = value;
+    }
+};
+
+/// Default is a fluent-constructor helper for Options
+template <typename T>
+struct Default {
+    /// The default value to apply to an Option
+    T value;
+
+    /// @param option the option to apply the default value to
+    template <typename O>
+    void Apply(O& option) {
+        option.default_value = value;
+    }
+};
+
+/// Deduction guide for Default
+template <typename T>
+Default(T) -> Default<T>;
+
+/// Option is the base class for all command line options
+class Option {
+  public:
+    /// An alias to std::string, used to hold error messages.
+    using Error = std::string;
+
+    /// Destructor
+    virtual ~Option();
+
+    /// @return the name of the option, without any leading hyphens.
+    /// Example: 'help'
+    virtual std::string Name() const = 0;
+
+    /// @return the alias name of the option, without any leading hyphens. (optional)
+    /// Example: 'flag'
+    virtual std::string Alias() const = 0;
+
+    /// @return the shorter name of the option, without any leading hyphens. (optional)
+    /// Example: 'h'
+    virtual std::string ShortName() const = 0;
+
+    /// @return a string describing the parameter that the option expects.
+    /// Empty represents no expected parameter.
+    virtual std::string Parameter() const = 0;
+
+    /// @return a description of the option.
+    /// Example: 'shows this message'
+    virtual std::string Description() const = 0;
+
+    /// @return the default value of the option, or an empty string if there is no default value.
+    virtual std::string DefaultValue() const = 0;
+
+    /// Sets the option value to the default (called before arguments are parsed)
+    virtual void SetDefault() = 0;
+
+    /// Parses the option's arguments from the list of command line arguments, removing the consumed
+    /// arguments before returning. @p arguments will have already had the option's name consumed
+    /// before calling.
+    /// @param arguments the queue of unprocessed arguments. Parse() may take from the front of @p
+    /// arguments.
+    /// @return empty Error if successfully parsed, otherwise an error string.
+    virtual Error Parse(std::deque<std::string_view>& arguments) = 0;
+
+  protected:
+    /// An empty string, used to represent no-error.
+    static constexpr const char* Success = "";
+
+    /// @param expected the expected value(s) for the option
+    /// @return an Error message for a missing argument
+    Error ErrMissingArgument(std::string expected) const {
+        Error err = "missing value for option '--" + Name() + "'";
+        if (!expected.empty()) {
+            err += "Expected: " + expected;
+        }
+        return err;
+    }
+
+    /// @param got the argument value provided
+    /// @param reason the reason the argument is invalid (optional)
+    /// @return an Error message for an invalid argument
+    Error ErrInvalidArgument(std::string_view got, std::string reason) const {
+        Error err = "invalid value '" + std::string(got) + "' for option '--" + Name() + "'";
+        if (!reason.empty()) {
+            err += "\n" + reason;
+        }
+        return err;
+    }
+};
+
+/// OptionSet is a set of Options, which can parse the command line arguments.
+class OptionSet {
+  public:
+    /// Unconsumed is a list of unconsumed command line arguments
+    using Unconsumed = utils::Vector<std::string_view, 8>;
+
+    /// Constructs and returns a new Option to be owned by the OptionSet
+    /// @tparam T the Option type
+    /// @tparam ARGS the constructor argument types
+    /// @param args the constructor arguments
+    /// @return the constructed Option
+    template <typename T, typename... ARGS>
+    T& Add(ARGS&&... args) {
+        return *options.Create<T>(std::forward<ARGS>(args)...);
+    }
+
+    /// Prints to @p out the description of all the command line options.
+    /// @param out the output stream
+    void ShowHelp(std::ostream& out);
+
+    /// Parses all the options in @p options.
+    /// @param err the error stream
+    /// @param arguments the command line arguments, excluding the initial executable name
+    /// @return a Result holding a list of arguments that were not consumed as options
+    Result<Unconsumed> Parse(std::ostream& err, utils::VectorRef<std::string_view> arguments);
+
+  private:
+    /// The list of options to parse
+    utils::BlockAllocator<Option, 1024> options;
+};
+
+/// ValueOption is an option that accepts a single value
+template <typename T>
+class ValueOption : public Option {
+    static constexpr bool is_bool = std::is_same_v<T, bool>;
+    static constexpr bool is_number =
+        !is_bool && (std::is_integral_v<T> || std::is_floating_point_v<T>);
+    static constexpr bool is_string = std::is_same_v<T, std::string>;
+    static_assert(is_bool || is_number || is_string, "unsupported data type");
+
+  public:
+    /// The name of the option, without any leading hyphens.
+    std::string name;
+    /// The alias name of the option, without any leading hyphens.
+    std::string alias;
+    /// The shorter name of the option, without any leading hyphens.
+    std::string short_name;
+    /// A description of the option.
+    std::string description;
+    /// The default value.
+    std::optional<T> default_value;
+    /// The option value. Populated with Parse().
+    std::optional<T> value;
+    /// A string describing the name of the option's value.
+    std::string parameter = "value";
+
+    /// Constructor
+    ValueOption() = default;
+
+    /// Constructor
+    /// @param option_name the option name
+    /// @param option_description the option description
+    /// @param settings a number of fluent-constructor values that configure the option
+    /// @see ShortName, Parameter, Default
+    template <typename... SETTINGS>
+    ValueOption(std::string option_name, std::string option_description, SETTINGS&&... settings)
+        : name(std::move(option_name)), description(std::move(option_description)) {
+        (settings.Apply(*this), ...);
+    }
+
+    std::string Name() const override { return name; }
+
+    std::string Alias() const override { return alias; }
+
+    std::string ShortName() const override { return short_name; }
+
+    std::string Parameter() const override { return parameter; }
+
+    std::string Description() const override { return description; }
+
+    std::string DefaultValue() const override {
+        return default_value.has_value() ? ToString(*default_value) : "";
+    }
+
+    void SetDefault() override { value = default_value; }
+
+    Error Parse(std::deque<std::string_view>& arguments) override {
+        TINT_BEGIN_DISABLE_WARNING(UNREACHABLE_CODE);
+
+        if (arguments.empty()) {
+            if constexpr (is_bool) {
+                // Treat as flag (--blah)
+                value = true;
+                return Success;
+            } else {
+                return ErrMissingArgument(parameter);
+            }
+        }
+
+        auto arg = arguments.front();
+
+        if constexpr (is_number) {
+            auto result = ParseNumber<T>(arg);
+            if (result) {
+                value = result.Get();
+                arguments.pop_front();
+                return Success;
+            }
+            if (result.Failure() == ParseNumberError::kResultOutOfRange) {
+                return ErrInvalidArgument(arg, "value out of range");
+            }
+            return ErrInvalidArgument(arg, "failed to parse value");
+        } else if constexpr (is_string) {
+            value = arg;
+            arguments.pop_front();
+            return Success;
+        } else if constexpr (is_bool) {
+            if (arg == "true") {
+                value = true;
+                arguments.pop_front();
+                return Success;
+            }
+            if (arg == "false") {
+                value = false;
+                arguments.pop_front();
+                return Success;
+            }
+            // Next argument is assumed to be another option, or unconsumed argument.
+            // Treat as flag (--blah)
+            value = true;
+            return Success;
+        }
+
+        TINT_END_DISABLE_WARNING(UNREACHABLE_CODE);
+    }
+};
+
+/// BoolOption is an alias to ValueOption<bool>
+using BoolOption = ValueOption<bool>;
+
+/// StringOption is an alias to ValueOption<std::string>
+using StringOption = ValueOption<std::string>;
+
+/// EnumName is a pair of enum value and name.
+/// @tparam ENUM the enum type
+template <typename ENUM>
+struct EnumName {
+    /// Constructor
+    EnumName() = default;
+
+    /// Constructor
+    /// @param v the enum value
+    /// @param n the name of the enum value
+    EnumName(ENUM v, std::string n) : value(v), name(std::move(n)) {}
+
+    /// the enum value
+    ENUM value;
+    /// the name of the enum value
+    std::string name;
+};
+
+/// Deduction guide for EnumName
+template <typename ENUM>
+EnumName(ENUM, std::string) -> EnumName<ENUM>;
+
+/// EnumOption is an option that accepts an enumerator of values
+template <typename ENUM>
+class EnumOption : public Option {
+  public:
+    /// The name of the option, without any leading hyphens.
+    std::string name;
+    /// The alias name of the option, without any leading hyphens.
+    std::string alias;
+    /// The shorter name of the option, without any leading hyphens.
+    std::string short_name;
+    /// A description of the option.
+    std::string description;
+    /// The enum options as a pair of enum value to name
+    utils::Vector<EnumName<ENUM>, 8> enum_names;
+    /// The default value.
+    std::optional<ENUM> default_value;
+    /// The option value. Populated with Parse().
+    std::optional<ENUM> value;
+
+    /// Constructor
+    EnumOption() = default;
+
+    /// Constructor
+    /// @param option_name the option name
+    /// @param option_description the option description
+    /// @param names The enum options as a pair of enum value to name
+    /// @param settings a number of fluent-constructor values that configure the option
+    /// @see ShortName, Parameter, Default
+    template <typename... SETTINGS>
+    EnumOption(std::string option_name,
+               std::string option_description,
+               utils::VectorRef<EnumName<ENUM>> names,
+               SETTINGS&&... settings)
+        : name(std::move(option_name)),
+          description(std::move(option_description)),
+          enum_names(std::move(names)) {
+        (settings.Apply(*this), ...);
+    }
+
+    std::string Name() const override { return name; }
+
+    std::string ShortName() const override { return short_name; }
+
+    std::string Alias() const override { return alias; }
+
+    std::string Parameter() const override { return PossibleValues("|"); }
+
+    std::string Description() const override { return description; }
+
+    std::string DefaultValue() const override {
+        for (auto& enum_name : enum_names) {
+            if (enum_name.value == default_value) {
+                return enum_name.name;
+            }
+        }
+        return "";
+    }
+
+    void SetDefault() override { value = default_value; }
+
+    Error Parse(std::deque<std::string_view>& arguments) override {
+        if (arguments.empty()) {
+            return ErrMissingArgument("one of: " + PossibleValues(", "));
+        }
+        auto& arg = arguments.front();
+        for (auto& enum_name : enum_names) {
+            if (enum_name.name == arg) {
+                value = enum_name.value;
+                arguments.pop_front();
+                return Success;
+            }
+        }
+        return ErrInvalidArgument(arg, "Must be one of: " + PossibleValues(", "));
+    }
+
+    /// @param delimiter the delimiter between each enum option
+    /// @returns the accepted enum names delimited with @p delimiter
+    std::string PossibleValues(std::string delimiter) const {
+        std::string out;
+        for (auto& enum_name : enum_names) {
+            if (!out.empty()) {
+                out += delimiter;
+            }
+            out += enum_name.name;
+        }
+        return out;
+    }
+};
+
+}  // namespace tint::utils::cli
+
+#endif  // SRC_TINT_UTILS_CLI_CLI_H_