diff options
| -rw-r--r-- | args.hxx | 199 | 
1 files changed, 194 insertions, 5 deletions
| @@ -2,13 +2,56 @@   * This code is released under the license described in the LICENSE file   */ +#include <algorithm> +#include <functional> +#include <list> +#include <sstream>  #include <string>  #include <vector> -#include <list> -#include <functional> +#include <exception>  namespace args  { +    class ParseError : public std::runtime_error +    { +        public: +            ParseError(const char *problem) : std::runtime_error(problem) {} +            virtual ~ParseError() {}; +    }; + +    // A class of "matchers", specifying short and long options that can possibly be matched +    class Matcher +    { +        private: +            std::vector<char> shortOpts; +            std::vector<std::string> longOpts; + +        public: +            // Specify short and long opts separately as iterators +            template <typename ShortIt, typename LongIt> +            Matcher(ShortIt shortOptsStart, ShortIt shortOptsEnd, LongIt longOptsStart, LongIt longOptsEnd) : +                shortOpts(shortOptsStart, shortOptsEnd), +                longOpts(longOptsStart, longOptsEnd) +            {} + +            // Specify short and long opts separately as iterables +            template <typename Short, typename Long> +            Matcher(Short &&shortIn, Long &&longIn) : +                Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn)) +            {} + +            bool Match(const char opt) const +            { +                return std::find(shortOpts.begin(), shortOpts.end(), opt) != shortOpts.end(); +            } + +            bool Match(const std::string &opt) const +            { +                return std::find(longOpts.begin(), longOpts.end(), opt) != longOpts.end(); +            } +    }; + +    // Base class for groups and individual argument types      class Base      {          protected: @@ -18,10 +61,25 @@ namespace args              Base() : matched(false) {}              virtual ~Base() {} +            virtual Base *Match(const std::string &arg) = 0; +            virtual Base *Match(const char arg) = 0; +              virtual bool Matched() const              {                  return matched;              } + +            operator bool() const +            { +                return matched; +            } +    }; + +    // Base class that takes arguments +    class ArgBase : public Base +    { +        public: +            virtual ~ArgBase() {}      };      class Group : public Base @@ -35,6 +93,32 @@ namespace args              Group(const std::function<bool(int, int)> &validator = Validators::DontCare) : validator(validator) {}              virtual ~Group() {} +            virtual Base *Match(const std::string &arg) override +            { +                for (Base *child: children) +                { +                    Base *match = child->Match(arg); +                    if (match) +                    { +                        return match; +                    } +                } +                return nullptr; +            } + +            virtual Base *Match(const char arg) override +            { +                for (Base *child: children) +                { +                    Base *match = child->Match(arg); +                    if (match) +                    { +                        return match; +                    } +                } +                return nullptr; +            } +              void Add(Base &child)              {                  children.emplace_back(&child); @@ -104,20 +188,79 @@ namespace args              std::string description;              std::string epilog; -            Group args; +            std::string longprefix; +            std::string shortprefix; + +            Group group;          public:              ArgumentParser( -                const std::string &prog,                  const std::string &description, -                const std::string &epilog = std::string()) : prog(prog), description(description), epilog(epilog) {} +                const std::string &longprefix = "--", +                const std::string &shortprefix = "-", +                const std::string &prog = std::string(), +                const std::string &epilog = std::string() +                ) : +                    prog(prog), +                    description(description), +                    epilog(epilog), +                    longprefix(longprefix), +                    shortprefix(shortprefix), +                    group(Group::Validators::DontCare) {} + +            void Add(Base &item) +            { +                group.Add(item); +            }              void ParseArgs(const std::vector<std::string> &args)              { +                for (auto it = std::begin(args); it != std::end(args); ++it) +                { +                    const std::string &chunk = *it; +                    if (chunk.find(longprefix) == 0 && chunk.size() > longprefix.size()) +                    { +                        const std::string argchunk(chunk.substr(longprefix.size())); +                        Base *base = group.Match(argchunk); +                        if (base) +                        { +                            // Do match logic here, specifically passing an +                            // argument, if necessary, to the base.  Query the +                            // base and see if it takes an argument +                        } else +                        { +                            std::ostringstream problem; +                            problem << "Argument could not be matched: " << chunk; +                            throw ParseError(problem.str().c_str()); +                        } +                    } else if (chunk.find(shortprefix) == 0 && chunk.size() > shortprefix.size()) +                    { +                        const std::string argchunk(chunk.substr(shortprefix.size())); +                        for (const char &arg: argchunk) +                        { +                            Base *base = group.Match(arg); +                            if (base) +                            { +                                // Do match logic here, specifically passing an +                                // argument, if necessary, to the base.  Query the +                                // base and see if it takes an argument +                            } else +                            { +                                std::ostringstream problem; +                                problem << "Argument could not be matched: " << arg; +                                throw ParseError(problem.str().c_str()); +                            } +                        } +                    } +                }              }              void ParseCLI(const int argc, const char * const * const argv)              { +                if (prog.empty()) +                { +                    prog.assign(argv[0]); +                }                  std::vector<std::string> args;                  for (int i = 1; i < argc; ++i)                  { @@ -126,4 +269,50 @@ namespace args                  ParseArgs(args);              }      }; + +    // Boolean argument matcher +    class Flag : public Base +    { +        private: +            Matcher matcher; + +        public: +            //template <typename ... Types> +            //Flag(std::string help, Types ...args): matcher(args...) {} + +            template <typename ... Types> +            Flag(ArgumentParser &parser, std::string help, Types&& ...args): matcher(args...) +            { +                parser.Add(*this); +            } + +            template <typename Short, typename Long> +            Flag(ArgumentParser &parser, std::string help, const std::initializer_list<Short> &shortIn, const std::initializer_list<Long> &longIn): matcher(shortIn, longIn) +            { +                parser.Add(*this); +            } + +            virtual ~Flag() {} + +            virtual Base *Match(const std::string &arg) override +            { +                if (matcher.Match(arg)) +                { +                    matched = true; +                    return this; +                } +                return nullptr; +            } + +            virtual Base *Match(const char arg) override +            { +                if (matcher.Match(arg)) +                { +                    matched = true; +                    return this; +                } +                return nullptr; +            } +    }; +  } | 
