summaryrefslogtreecommitdiff
path: root/tb/top
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2023-10-05 00:36:37 -0600
committerAlejandro Soto <alejandro@34project.org>2023-10-05 13:07:57 -0600
commit4acd900c4602db0353d11bf6841ddadfd80c57b8 (patch)
treefe24637ccd16fefcf25f0c8d8e65b01d19a1760c /tb/top
parentd173727c1ed34652613fc046d522a43ac9ef015e (diff)
Makefile, tb: add support for cocotb
Diffstat (limited to 'tb/top')
-rw-r--r--tb/top/__init__.py0
-rw-r--r--tb/top/conspiracion.cpp84
-rw-r--r--tb/top/conspiracion/args.hxx4572
-rw-r--r--tb/top/conspiracion/platform.sv430
-rw-r--r--tb/top/conspiracion/sim_slave.cpp60
-rw-r--r--tb/top/conspiracion/sim_slave.hpp36
-rw-r--r--tb/top/conspiracion/sim_slave.sv28
-rw-r--r--tb/top/conspiracion/vga_domain.sv36
-rw-r--r--tb/top/decode_test.cpp101
-rw-r--r--tb/top/fetch_test.cpp166
-rw-r--r--tb/top/hps_sdram_test.cpp93
-rw-r--r--tb/top/mul_test.cpp71
-rw-r--r--tb/top/smp_sim.cpp0
-rw-r--r--tb/top/smp_sim.py0
14 files changed, 5204 insertions, 473 deletions
diff --git a/tb/top/__init__.py b/tb/top/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tb/top/__init__.py
diff --git a/tb/top/conspiracion.cpp b/tb/top/conspiracion.cpp
index a0de8f4..198cc30 100644
--- a/tb/top/conspiracion.cpp
+++ b/tb/top/conspiracion.cpp
@@ -16,41 +16,41 @@
#include <verilated_fst_c.h>
#endif
-#include "Vconspiracion.h"
-#include "Vconspiracion_arm810.h"
-#include "Vconspiracion_conspiracion.h"
-#include "Vconspiracion_platform.h"
-#include "Vconspiracion_sim_slave.h"
-#include "Vconspiracion_vga_domain.h"
-#include "Vconspiracion_core.h"
-#include "Vconspiracion_core_control.h"
-#include "Vconspiracion_core_control_issue.h"
-#include "Vconspiracion_core_cp15_domain.h"
-#include "Vconspiracion_core_cp15_far.h"
-#include "Vconspiracion_core_cp15_fsr.h"
-#include "Vconspiracion_core_cp15_syscfg.h"
-#include "Vconspiracion_core_cp15_ttbr.h"
-#include "Vconspiracion_core_cp15.h"
-#include "Vconspiracion_core_fetch.h"
-#include "Vconspiracion_core_mmu.h"
-#include "Vconspiracion_core_psr.h"
-#include "Vconspiracion_core_regs.h"
-#include "Vconspiracion_core_reg_file.h"
-#include "Vconspiracion_cache.h"
-#include "Vconspiracion_cache__T1.h"
-#include "Vconspiracion_cache_sram.h"
-
-#include "../args.hxx"
-
-#include "../avalon.hpp"
-#include "../const.hpp"
-#include "../mem.hpp"
-#include "../jtag_uart.hpp"
-#include "../interval_timer.hpp"
-#include "../null.hpp"
-#include "../sim_slave.hpp"
-#include "../window.hpp"
-#include "../vga.hpp"
+#include "Vtop.h"
+#include "Vtop_arm810.h"
+#include "Vtop_conspiracion.h"
+#include "Vtop_platform.h"
+#include "Vtop_sim_slave.h"
+#include "Vtop_vga_domain.h"
+#include "Vtop_core.h"
+#include "Vtop_core_control.h"
+#include "Vtop_core_control_issue.h"
+#include "Vtop_core_cp15_domain.h"
+#include "Vtop_core_cp15_far.h"
+#include "Vtop_core_cp15_fsr.h"
+#include "Vtop_core_cp15_syscfg.h"
+#include "Vtop_core_cp15_ttbr.h"
+#include "Vtop_core_cp15.h"
+#include "Vtop_core_fetch.h"
+#include "Vtop_core_mmu.h"
+#include "Vtop_core_psr.h"
+#include "Vtop_core_regs.h"
+#include "Vtop_core_reg_file.h"
+#include "Vtop_cache.h"
+#include "Vtop_cache__T1.h"
+#include "Vtop_cache_sram.h"
+
+#include "args.hxx"
+
+#include "avalon.hpp"
+#include "const.hpp"
+#include "mem.hpp"
+#include "jtag_uart.hpp"
+#include "interval_timer.hpp"
+#include "null.hpp"
+#include "sim_slave.hpp"
+#include "window.hpp"
+#include "vga.hpp"
namespace
{
@@ -307,7 +307,7 @@ int main(int argc, char **argv)
dup2(*control_fd, STDERR_FILENO);
}
- Vconspiracion top;
+ Vtop top;
#if VM_TRACE
VerilatedFstC trace;
@@ -335,8 +335,8 @@ int main(int argc, char **argv)
null vram_null(0x3800'0000, 64 << 20, 2);
window vram_window(vram, 0x0000'0000);
- Vconspiracion_platform &plat = *top.conspiracion->plat;
- display<Vconspiracion_vga_domain> vga
+ Vtop_platform &plat = *top.conspiracion->plat;
+ display<Vtop_vga_domain> vga
(
*plat.vga, 0x3800'0000, 25'175'000, 50'000'000
);
@@ -347,8 +347,8 @@ int main(int argc, char **argv)
sim_slave dbg_3(*plat.smp_dbg_3, 0x3013'0000, 32);
sim_slave smp_ctrl(*plat.smp_sim, 0x3014'0000, 4);
- interconnect<Vconspiracion_platform> avl(plat);
- //interconnect<Vconspiracion_vga_domain> avl_vga(plat->vga);
+ interconnect<Vtop_platform> avl(plat);
+ //interconnect<Vtop_vga_domain> avl_vga(plat->vga);
std::vector<const_map> consts;
for(const auto &init : *const_)
@@ -411,14 +411,14 @@ int main(int argc, char **argv)
std::fclose(img_file);
}
- Vconspiracion_arm810 *const cores[] = {
+ Vtop_arm810 *const cores[] = {
plat.cpu_0->cpu,
plat.cpu_1->cpu,
plat.cpu_2->cpu,
plat.cpu_3->cpu
};
- Vconspiracion_cache_sram *const caches[] = {
+ Vtop_cache_sram *const caches[] = {
plat.cache_0->sram,
plat.cache_1->sram,
plat.cache_2->sram,
diff --git a/tb/top/conspiracion/args.hxx b/tb/top/conspiracion/args.hxx
new file mode 100644
index 0000000..69ccbf6
--- /dev/null
+++ b/tb/top/conspiracion/args.hxx
@@ -0,0 +1,4572 @@
+/* A simple header-only C++ argument parser library.
+ *
+ * https://github.com/Taywee/args
+ *
+ * Copyright (c) 2016-2021 Taylor C. Richberger <taywee@gmx.com> and Pavel
+ * Belikov
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \file args.hxx
+ * \brief this single-header lets you use all of the args functionality
+ *
+ * The important stuff is done inside the args namespace
+ */
+
+#ifndef ARGS_HXX
+#define ARGS_HXX
+
+#define ARGS_VERSION "6.3.0"
+#define ARGS_VERSION_MAJOR 6
+#define ARGS_VERSION_MINOR 3
+#define ARGS_VERSION_PATCH 0
+
+#include <algorithm>
+#include <iterator>
+#include <exception>
+#include <functional>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+#include <type_traits>
+#include <cstddef>
+#include <cctype>
+#include <iostream>
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+#define noexcept
+#endif
+
+#ifdef ARGS_TESTNAMESPACE
+namespace argstest
+{
+#else
+
+/** \namespace args
+ * \brief contains all the functionality of the args library
+ */
+namespace args
+{
+#endif
+ /** Getter to grab the value from the argument type.
+ *
+ * If the Get() function of the type returns a reference, so does this, and
+ * the value will be modifiable.
+ */
+ template <typename Option>
+ auto get(Option &option_) -> decltype(option_.Get())
+ {
+ return option_.Get();
+ }
+
+ /** (INTERNAL) Count UTF-8 glyphs
+ *
+ * This is not reliable, and will fail for combinatory glyphs, but it's
+ * good enough here for now.
+ *
+ * \param string The string to count glyphs from
+ * \return The UTF-8 glyphs in the string
+ */
+ inline std::string::size_type Glyphs(const std::string &string_)
+ {
+ std::string::size_type length = 0;
+ for (const char c: string_)
+ {
+ if ((c & 0xc0) != 0x80)
+ {
+ ++length;
+ }
+ }
+ return length;
+ }
+
+ /** (INTERNAL) Wrap a vector of words into a vector of lines
+ *
+ * Empty words are skipped. Word "\n" forces wrapping.
+ *
+ * \param begin The begin iterator
+ * \param end The end iterator
+ * \param width The width of the body
+ * \param firstlinewidth the width of the first line, defaults to the width of the body
+ * \param firstlineindent the indent of the first line, defaults to 0
+ * \return the vector of lines
+ */
+ template <typename It>
+ inline std::vector<std::string> Wrap(It begin,
+ It end,
+ const std::string::size_type width,
+ std::string::size_type firstlinewidth = 0,
+ std::string::size_type firstlineindent = 0)
+ {
+ std::vector<std::string> output;
+ std::string line(firstlineindent, ' ');
+ bool empty = true;
+
+ if (firstlinewidth == 0)
+ {
+ firstlinewidth = width;
+ }
+
+ auto currentwidth = firstlinewidth;
+
+ for (auto it = begin; it != end; ++it)
+ {
+ if (it->empty())
+ {
+ continue;
+ }
+
+ if (*it == "\n")
+ {
+ if (!empty)
+ {
+ output.push_back(line);
+ line.clear();
+ empty = true;
+ currentwidth = width;
+ }
+
+ continue;
+ }
+
+ auto itemsize = Glyphs(*it);
+ if ((line.length() + 1 + itemsize) > currentwidth)
+ {
+ if (!empty)
+ {
+ output.push_back(line);
+ line.clear();
+ empty = true;
+ currentwidth = width;
+ }
+ }
+
+ if (itemsize > 0)
+ {
+ if (!empty)
+ {
+ line += ' ';
+ }
+
+ line += *it;
+ empty = false;
+ }
+ }
+
+ if (!empty)
+ {
+ output.push_back(line);
+ }
+
+ return output;
+ }
+
+ namespace detail
+ {
+ template <typename T>
+ std::string Join(const T& array, const std::string &delimiter)
+ {
+ std::string res;
+ for (auto &element : array)
+ {
+ if (!res.empty())
+ {
+ res += delimiter;
+ }
+
+ res += element;
+ }
+
+ return res;
+ }
+ }
+
+ /** (INTERNAL) Wrap a string into a vector of lines
+ *
+ * This is quick and hacky, but works well enough. You can specify a
+ * different width for the first line
+ *
+ * \param width The width of the body
+ * \param firstlinewid the width of the first line, defaults to the width of the body
+ * \return the vector of lines
+ */
+ inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0)
+ {
+ // Preserve existing line breaks
+ const auto newlineloc = in.find('\n');
+ if (newlineloc != in.npos)
+ {
+ auto first = Wrap(std::string(in, 0, newlineloc), width);
+ auto second = Wrap(std::string(in, newlineloc + 1), width);
+ first.insert(
+ std::end(first),
+ std::make_move_iterator(std::begin(second)),
+ std::make_move_iterator(std::end(second)));
+ return first;
+ }
+
+ std::istringstream stream(in);
+ std::string::size_type indent = 0;
+
+ for (auto c : in)
+ {
+ if (!std::isspace(static_cast<unsigned char>(c)))
+ {
+ break;
+ }
+ ++indent;
+ }
+
+ return Wrap(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>(),
+ width, firstlinewidth, indent);
+ }
+
+#ifdef ARGS_NOEXCEPT
+ /// Error class, for when ARGS_NOEXCEPT is defined
+ enum class Error
+ {
+ None,
+ Usage,
+ Parse,
+ Validation,
+ Required,
+ Map,
+ Extra,
+ Help,
+ Subparser,
+ Completion,
+ };
+#else
+ /** Base error class
+ */
+ class Error : public std::runtime_error
+ {
+ public:
+ Error(const std::string &problem) : std::runtime_error(problem) {}
+ virtual ~Error() {}
+ };
+
+ /** Errors that occur during usage
+ */
+ class UsageError : public Error
+ {
+ public:
+ UsageError(const std::string &problem) : Error(problem) {}
+ virtual ~UsageError() {}
+ };
+
+ /** Errors that occur during regular parsing
+ */
+ class ParseError : public Error
+ {
+ public:
+ ParseError(const std::string &problem) : Error(problem) {}
+ virtual ~ParseError() {}
+ };
+
+ /** Errors that are detected from group validation after parsing finishes
+ */
+ class ValidationError : public Error
+ {
+ public:
+ ValidationError(const std::string &problem) : Error(problem) {}
+ virtual ~ValidationError() {}
+ };
+
+ /** Errors that when a required flag is omitted
+ */
+ class RequiredError : public ValidationError
+ {
+ public:
+ RequiredError(const std::string &problem) : ValidationError(problem) {}
+ virtual ~RequiredError() {}
+ };
+
+ /** Errors in map lookups
+ */
+ class MapError : public ParseError
+ {
+ public:
+ MapError(const std::string &problem) : ParseError(problem) {}
+ virtual ~MapError() {}
+ };
+
+ /** Error that occurs when a singular flag is specified multiple times
+ */
+ class ExtraError : public ParseError
+ {
+ public:
+ ExtraError(const std::string &problem) : ParseError(problem) {}
+ virtual ~ExtraError() {}
+ };
+
+ /** An exception that indicates that the user has requested help
+ */
+ class Help : public Error
+ {
+ public:
+ Help(const std::string &flag) : Error(flag) {}
+ virtual ~Help() {}
+ };
+
+ /** (INTERNAL) An exception that emulates coroutine-like control flow for subparsers.
+ */
+ class SubparserError : public Error
+ {
+ public:
+ SubparserError() : Error("") {}
+ virtual ~SubparserError() {}
+ };
+
+ /** An exception that contains autocompletion reply
+ */
+ class Completion : public Error
+ {
+ public:
+ Completion(const std::string &flag) : Error(flag) {}
+ virtual ~Completion() {}
+ };
+#endif
+
+ /** A simple unified option type for unified initializer lists for the Matcher class.
+ */
+ struct EitherFlag
+ {
+ const bool isShort;
+ const char shortFlag;
+ const std::string longFlag;
+ EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {}
+ EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {}
+ EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {}
+
+ /** Get just the long flags from an initializer list of EitherFlags
+ */
+ static std::unordered_set<std::string> GetLong(std::initializer_list<EitherFlag> flags)
+ {
+ std::unordered_set<std::string> longFlags;
+ for (const EitherFlag &flag: flags)
+ {
+ if (!flag.isShort)
+ {
+ longFlags.insert(flag.longFlag);
+ }
+ }
+ return longFlags;
+ }
+
+ /** Get just the short flags from an initializer list of EitherFlags
+ */
+ static std::unordered_set<char> GetShort(std::initializer_list<EitherFlag> flags)
+ {
+ std::unordered_set<char> shortFlags;
+ for (const EitherFlag &flag: flags)
+ {
+ if (flag.isShort)
+ {
+ shortFlags.insert(flag.shortFlag);
+ }
+ }
+ return shortFlags;
+ }
+
+ std::string str() const
+ {
+ return isShort ? std::string(1, shortFlag) : longFlag;
+ }
+
+ std::string str(const std::string &shortPrefix, const std::string &longPrefix) const
+ {
+ return isShort ? shortPrefix + std::string(1, shortFlag) : longPrefix + longFlag;
+ }
+ };
+
+
+
+ /** A class of "matchers", specifying short and flags that can possibly be
+ * matched.
+ *
+ * This is supposed to be constructed and then passed in, not used directly
+ * from user code.
+ */
+ class Matcher
+ {
+ private:
+ const std::unordered_set<char> shortFlags;
+ const std::unordered_set<std::string> longFlags;
+
+ public:
+ /** Specify short and long flags separately as iterators
+ *
+ * ex: `args::Matcher(shortFlags.begin(), shortFlags.end(), longFlags.begin(), longFlags.end())`
+ */
+ template <typename ShortIt, typename LongIt>
+ Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) :
+ shortFlags(shortFlagsStart, shortFlagsEnd),
+ longFlags(longFlagsStart, longFlagsEnd)
+ {
+ if (shortFlags.empty() && longFlags.empty())
+ {
+#ifndef ARGS_NOEXCEPT
+ throw UsageError("empty Matcher");
+#endif
+ }
+ }
+
+#ifdef ARGS_NOEXCEPT
+ /// Only for ARGS_NOEXCEPT
+ Error GetError() const noexcept
+ {
+ return shortFlags.empty() && longFlags.empty() ? Error::Usage : Error::None;
+ }
+#endif
+
+ /** Specify short and long flags separately as iterables
+ *
+ * ex: `args::Matcher(shortFlags, longFlags)`
+ */
+ template <typename Short, typename Long>
+ Matcher(Short &&shortIn, Long &&longIn) :
+ Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn))
+ {}
+
+ /** Specify a mixed single initializer-list of both short and long flags
+ *
+ * This is the fancy one. It takes a single initializer list of
+ * any number of any mixed kinds of flags. Chars are
+ * automatically interpreted as short flags, and strings are
+ * automatically interpreted as long flags:
+ *
+ * args::Matcher{'a'}
+ * args::Matcher{"foo"}
+ * args::Matcher{'h', "help"}
+ * args::Matcher{"foo", 'f', 'F', "FoO"}
+ */
+ Matcher(std::initializer_list<EitherFlag> in) :
+ Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {}
+
+ Matcher(Matcher &&other) noexcept : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags))
+ {}
+
+ ~Matcher() {}
+
+ /** (INTERNAL) Check if there is a match of a short flag
+ */
+ bool Match(const char flag) const
+ {
+ return shortFlags.find(flag) != shortFlags.end();
+ }
+
+ /** (INTERNAL) Check if there is a match of a long flag
+ */
+ bool Match(const std::string &flag) const
+ {
+ return longFlags.find(flag) != longFlags.end();
+ }
+
+ /** (INTERNAL) Check if there is a match of a flag
+ */
+ bool Match(const EitherFlag &flag) const
+ {
+ return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag);
+ }
+
+ /** (INTERNAL) Get all flag strings as a vector, with the prefixes embedded
+ */
+ std::vector<EitherFlag> GetFlagStrings() const
+ {
+ std::vector<EitherFlag> flagStrings;
+ flagStrings.reserve(shortFlags.size() + longFlags.size());
+ for (const char flag: shortFlags)
+ {
+ flagStrings.emplace_back(flag);
+ }
+ for (const std::string &flag: longFlags)
+ {
+ flagStrings.emplace_back(flag);
+ }
+ return flagStrings;
+ }
+
+ /** (INTERNAL) Get long flag if it exists or any short flag
+ */
+ EitherFlag GetLongOrAny() const
+ {
+ if (!longFlags.empty())
+ {
+ return *longFlags.begin();
+ }
+
+ if (!shortFlags.empty())
+ {
+ return *shortFlags.begin();
+ }
+
+ // should be unreachable
+ return ' ';
+ }
+
+ /** (INTERNAL) Get short flag if it exists or any long flag
+ */
+ EitherFlag GetShortOrAny() const
+ {
+ if (!shortFlags.empty())
+ {
+ return *shortFlags.begin();
+ }
+
+ if (!longFlags.empty())
+ {
+ return *longFlags.begin();
+ }
+
+ // should be unreachable
+ return ' ';
+ }
+ };
+
+ /** Attributes for flags.
+ */
+ enum class Options
+ {
+ /** Default options.
+ */
+ None = 0x0,
+
+ /** Flag can't be passed multiple times.
+ */
+ Single = 0x01,
+
+ /** Flag can't be omitted.
+ */
+ Required = 0x02,
+
+ /** Flag is excluded from usage line.
+ */
+ HiddenFromUsage = 0x04,
+
+ /** Flag is excluded from options help.
+ */
+ HiddenFromDescription = 0x08,
+
+ /** Flag is global and can be used in any subcommand.
+ */
+ Global = 0x10,
+
+ /** Flag stops a parser.
+ */
+ KickOut = 0x20,
+
+ /** Flag is excluded from auto completion.
+ */
+ HiddenFromCompletion = 0x40,
+
+ /** Flag is excluded from options help and usage line
+ */
+ Hidden = HiddenFromUsage | HiddenFromDescription | HiddenFromCompletion,
+ };
+
+ inline Options operator | (Options lhs, Options rhs)
+ {
+ return static_cast<Options>(static_cast<int>(lhs) | static_cast<int>(rhs));
+ }
+
+ inline Options operator & (Options lhs, Options rhs)
+ {
+ return static_cast<Options>(static_cast<int>(lhs) & static_cast<int>(rhs));
+ }
+
+ class FlagBase;
+ class PositionalBase;
+ class Command;
+ class ArgumentParser;
+
+ /** A simple structure of parameters for easy user-modifyable help menus
+ */
+ struct HelpParams
+ {
+ /** The width of the help menu
+ */
+ unsigned int width = 80;
+ /** The indent of the program line
+ */
+ unsigned int progindent = 2;
+ /** The indent of the program trailing lines for long parameters
+ */
+ unsigned int progtailindent = 4;
+ /** The indent of the description and epilogs
+ */
+ unsigned int descriptionindent = 4;
+ /** The indent of the flags
+ */
+ unsigned int flagindent = 6;
+ /** The indent of the flag descriptions
+ */
+ unsigned int helpindent = 40;
+ /** The additional indent each group adds
+ */
+ unsigned int eachgroupindent = 2;
+
+ /** The minimum gutter between each flag and its help
+ */
+ unsigned int gutter = 1;
+
+ /** Show the terminator when both options and positional parameters are present
+ */
+ bool showTerminator = true;
+
+ /** Show the {OPTIONS} on the prog line when this is true
+ */
+ bool showProglineOptions = true;
+
+ /** Show the positionals on the prog line when this is true
+ */
+ bool showProglinePositionals = true;
+
+ /** The prefix for short flags
+ */
+ std::string shortPrefix;
+
+ /** The prefix for long flags
+ */
+ std::string longPrefix;
+
+ /** The separator for short flags
+ */
+ std::string shortSeparator;
+
+ /** The separator for long flags
+ */
+ std::string longSeparator;
+
+ /** The program name for help generation
+ */
+ std::string programName;
+
+ /** Show command's flags
+ */
+ bool showCommandChildren = false;
+
+ /** Show command's descriptions and epilog
+ */
+ bool showCommandFullHelp = false;
+
+ /** The postfix for progline when showProglineOptions is true and command has any flags
+ */
+ std::string proglineOptions = "{OPTIONS}";
+
+ /** The prefix for progline when command has any subcommands
+ */
+ std::string proglineCommand = "COMMAND";
+
+ /** The prefix for progline value
+ */
+ std::string proglineValueOpen = " <";
+
+ /** The postfix for progline value
+ */
+ std::string proglineValueClose = ">";
+
+ /** The prefix for progline required argument
+ */
+ std::string proglineRequiredOpen = "";
+
+ /** The postfix for progline required argument
+ */
+ std::string proglineRequiredClose = "";
+
+ /** The prefix for progline non-required argument
+ */
+ std::string proglineNonrequiredOpen = "[";
+
+ /** The postfix for progline non-required argument
+ */
+ std::string proglineNonrequiredClose = "]";
+
+ /** Show flags in program line
+ */
+ bool proglineShowFlags = false;
+
+ /** Use short flags in program lines when possible
+ */
+ bool proglinePreferShortFlags = false;
+
+ /** Program line prefix
+ */
+ std::string usageString;
+
+ /** String shown in help before flags descriptions
+ */
+ std::string optionsString = "OPTIONS:";
+
+ /** Display value name after all the long and short flags
+ */
+ bool useValueNameOnce = false;
+
+ /** Show value name
+ */
+ bool showValueName = true;
+
+ /** Add newline before flag description
+ */
+ bool addNewlineBeforeDescription = false;
+
+ /** The prefix for option value
+ */
+ std::string valueOpen = "[";
+
+ /** The postfix for option value
+ */
+ std::string valueClose = "]";
+
+ /** Add choices to argument description
+ */
+ bool addChoices = false;
+
+ /** The prefix for choices
+ */
+ std::string choiceString = "\nOne of: ";
+
+ /** Add default values to argument description
+ */
+ bool addDefault = false;
+
+ /** The prefix for default values
+ */
+ std::string defaultString = "\nDefault: ";
+ };
+
+ /** A number of arguments which can be consumed by an option.
+ *
+ * Represents a closed interval [min, max].
+ */
+ struct Nargs
+ {
+ const size_t min;
+ const size_t max;
+
+ Nargs(size_t min_, size_t max_) : min{min_}, max{max_}
+ {
+#ifndef ARGS_NOEXCEPT
+ if (max < min)
+ {
+ throw UsageError("Nargs: max > min");
+ }
+#endif
+ }
+
+ Nargs(size_t num_) : min{num_}, max{num_}
+ {
+ }
+
+ friend bool operator == (const Nargs &lhs, const Nargs &rhs)
+ {
+ return lhs.min == rhs.min && lhs.max == rhs.max;
+ }
+
+ friend bool operator != (const Nargs &lhs, const Nargs &rhs)
+ {
+ return !(lhs == rhs);
+ }
+ };
+
+ /** Base class for all match types
+ */
+ class Base
+ {
+ private:
+ Options options = {};
+
+ protected:
+ bool matched = false;
+ const std::string help;
+#ifdef ARGS_NOEXCEPT
+ /// Only for ARGS_NOEXCEPT
+ mutable Error error = Error::None;
+ mutable std::string errorMsg;
+#endif
+
+ public:
+ Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {}
+ virtual ~Base() {}
+
+ Options GetOptions() const noexcept
+ {
+ return options;
+ }
+
+ bool IsRequired() const noexcept
+ {
+ return (GetOptions() & Options::Required) != Options::None;
+ }
+
+ virtual bool Matched() const noexcept
+ {
+ return matched;
+ }
+
+ virtual void Validate(const std::string &, const std::string &) const
+ {
+ }
+
+ operator bool() const noexcept
+ {
+ return Matched();
+ }
+
+ virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &, const unsigned indentLevel) const
+ {
+ std::tuple<std::string, std::string, unsigned> description;
+ std::get<1>(description) = help;
+ std::get<2>(description) = indentLevel;
+ return { std::move(description) };
+ }
+
+ virtual std::vector<Command*> GetCommands()
+ {
+ return {};
+ }
+
+ virtual bool IsGroup() const
+ {
+ return false;
+ }
+
+ virtual FlagBase *Match(const EitherFlag &)
+ {
+ return nullptr;
+ }
+
+ virtual PositionalBase *GetNextPositional()
+ {
+ return nullptr;
+ }
+
+ virtual std::vector<FlagBase*> GetAllFlags()
+ {
+ return {};
+ }
+
+ virtual bool HasFlag() const
+ {
+ return false;
+ }
+
+ virtual bool HasPositional() const
+ {
+ return false;
+ }
+
+ virtual bool HasCommand() const
+ {
+ return false;
+ }
+
+ virtual std::vector<std::string> GetProgramLine(const HelpParams &) const
+ {
+ return {};
+ }
+
+ /// Sets a kick-out value for building subparsers
+ void KickOut(bool kickout_) noexcept
+ {
+ if (kickout_)
+ {
+ options = options | Options::KickOut;
+ }
+ else
+ {
+ options = static_cast<Options>(static_cast<int>(options) & ~static_cast<int>(Options::KickOut));
+ }
+ }
+
+ /// Gets the kick-out value for building subparsers
+ bool KickOut() const noexcept
+ {
+ return (options & Options::KickOut) != Options::None;
+ }
+
+ virtual void Reset() noexcept
+ {
+ matched = false;
+#ifdef ARGS_NOEXCEPT
+ error = Error::None;
+ errorMsg.clear();
+#endif
+ }
+
+#ifdef ARGS_NOEXCEPT
+ /// Only for ARGS_NOEXCEPT
+ virtual Error GetError() const
+ {
+ return error;
+ }
+
+ /// Only for ARGS_NOEXCEPT
+ std::string GetErrorMsg() const
+ {
+ return errorMsg;
+ }
+#endif
+ };
+
+ /** Base class for all match types that have a name
+ */
+ class NamedBase : public Base
+ {
+ protected:
+ const std::string name;
+ bool kickout = false;
+ std::string defaultString;
+ bool defaultStringManual = false;
+ std::vector<std::string> choicesStrings;
+ bool choicesStringManual = false;
+
+ virtual std::string GetDefaultString(const HelpParams&) const { return {}; }
+
+ virtual std::vector<std::string> GetChoicesStrings(const HelpParams&) const { return {}; }
+
+ virtual std::string GetNameString(const HelpParams&) const { return Name(); }
+
+ void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const
+ {
+ if (isManual && !manual.empty())
+ {
+ dest += str;
+ dest += manual;
+ }
+ else if (!isManual && isGenerated && !generated.empty())
+ {
+ dest += str;
+ dest += generated;
+ }
+ }
+
+ public:
+ NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {}
+ virtual ~NamedBase() {}
+
+ /** Sets default value string that will be added to argument description.
+ * Use empty string to disable it for this argument.
+ */
+ void HelpDefault(const std::string &str)
+ {
+ defaultStringManual = true;
+ defaultString = str;
+ }
+
+ /** Gets default value string that will be added to argument description.
+ */
+ std::string HelpDefault(const HelpParams &params) const
+ {
+ return defaultStringManual ? defaultString : GetDefaultString(params);
+ }
+
+ /** Sets choices strings that will be added to argument description.
+ * Use empty vector to disable it for this argument.
+ */
+ void HelpChoices(const std::vector<std::string> &array)
+ {
+ choicesStringManual = true;
+ choicesStrings = array;
+ }
+
+ /** Gets choices strings that will be added to argument description.
+ */
+ std::vector<std::string> HelpChoices(const HelpParams &params) const
+ {
+ return choicesStringManual ? choicesStrings : GetChoicesStrings(params);
+ }
+
+ virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned indentLevel) const override
+ {
+ std::tuple<std::string, std::string, unsigned> description;
+ std::get<0>(description) = GetNameString(params);
+ std::get<1>(description) = help;
+ std::get<2>(description) = indentLevel;
+
+ AddDescriptionPostfix(std::get<1>(description), choicesStringManual, detail::Join(choicesStrings, ", "), params.addChoices, detail::Join(GetChoicesStrings(params), ", "), params.choiceString);
+ AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString);
+
+ return { std::move(description) };
+ }
+
+ virtual std::string Name() const
+ {
+ return name;
+ }
+ };
+
+ namespace detail
+ {
+ template<typename T>
+ using vector = std::vector<T, std::allocator<T>>;
+
+ template<typename K, typename T>
+ using unordered_map = std::unordered_map<K, T, std::hash<K>,
+ std::equal_to<K>, std::allocator<std::pair<const K, T> > >;
+
+ template<typename S, typename T>
+ class is_streamable
+ {
+ template<typename SS, typename TT>
+ static auto test(int)
+ -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
+
+ template<typename, typename>
+ static auto test(...) -> std::false_type;
+
+ public:
+ using type = decltype(test<S,T>(0));
+ };
+
+ template <typename T>
+ using IsConvertableToString = typename is_streamable<std::ostringstream, T>::type;
+
+ template <typename T>
+ typename std::enable_if<IsConvertableToString<T>::value, std::string>::type
+ ToString(const T &value)
+ {
+ std::ostringstream s;
+ s << value;
+ return s.str();
+ }
+
+ template <typename T>
+ typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type
+ ToString(const T &)
+ {
+ return {};
+ }
+
+ template <typename T>
+ std::vector<std::string> MapKeysToStrings(const T &map)
+ {
+ std::vector<std::string> res;
+ using K = typename std::decay<decltype(std::begin(map)->first)>::type;
+ if (IsConvertableToString<K>::value)
+ {
+ for (const auto &p : map)
+ {
+ res.push_back(detail::ToString(p.first));
+ }
+
+ std::sort(res.begin(), res.end());
+ }
+ return res;
+ }
+ }
+
+ /** Base class for all flag options
+ */
+ class FlagBase : public NamedBase
+ {
+ protected:
+ const Matcher matcher;
+
+ virtual std::string GetNameString(const HelpParams &params) const override
+ {
+ const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name();
+ std::string flags;
+ const auto flagStrings = matcher.GetFlagStrings();
+ const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce;
+ for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it)
+ {
+ auto &flag = *it;
+ if (it != flagStrings.begin())
+ {
+ flags += ", ";
+ }
+
+ flags += flag.isShort ? params.shortPrefix : params.longPrefix;
+ flags += flag.str();
+
+ if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end()))
+ {
+ flags += flag.isShort ? params.shortSeparator : params.longSeparator;
+ flags += params.valueOpen + postfix + params.valueClose;
+ }
+ }
+
+ return flags;
+ }
+
+ public:
+ FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : NamedBase(name_, help_, extraError_ ? Options::Single : Options()), matcher(std::move(matcher_)) {}
+
+ FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {}
+
+ virtual ~FlagBase() {}
+
+ virtual FlagBase *Match(const EitherFlag &flag) override
+ {
+ if (matcher.Match(flag))
+ {
+ if ((GetOptions() & Options::Single) != Options::None && matched)
+ {
+ std::ostringstream problem;
+ problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once";
+#ifdef ARGS_NOEXCEPT
+ error = Error::Extra;
+ errorMsg = problem.str();
+#else
+ throw ExtraError(problem.str());
+#endif
+ }
+ matched = true;
+ return this;
+ }
+ return nullptr;
+ }
+
+ virtual std::vector<FlagBase*> GetAllFlags() override
+ {
+ return { this };
+ }
+
+ const Matcher &GetMatcher() const
+ {
+ return matcher;
+ }
+
+ virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
+ {
+ if (!Matched() && IsRequired())
+ {
+ std::ostringstream problem;
+ problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required";
+#ifdef ARGS_NOEXCEPT
+ error = Error::Required;
+ errorMsg = problem.str();
+#else
+ throw RequiredError(problem.str());
+#endif
+ }
+ }
+
+ virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
+ {
+ if (!params.proglineShowFlags)
+ {
+ return {};
+ }
+
+ const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name();
+ const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny();
+ std::string res = flag.str(params.shortPrefix, params.longPrefix);
+ if (!postfix.empty())
+ {
+ res += params.proglineValueOpen + postfix + params.proglineValueClose;
+ }
+
+ return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose
+ : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose };
+ }
+
+ virtual bool HasFlag() const override
+ {
+ return true;
+ }
+
+#ifdef ARGS_NOEXCEPT
+ /// Only for ARGS_NOEXCEPT
+ virtual Error GetError() const override
+ {
+ const auto nargs = NumberOfArguments();
+ if (nargs.min > nargs.max)
+ {
+ return Error::Usage;
+ }
+
+ const auto matcherError = matcher.GetError();
+ if (matcherError != Error::None)
+ {
+ return matcherError;
+ }
+
+ return error;
+ }
+#endif
+
+ /** Defines how many values can be consumed by this option.
+ *
+ * \return closed interval [min, max]
+ */
+ virtual Nargs NumberOfArguments() const noexcept = 0;
+
+ /** Parse values of this option.
+ *
+ * \param value Vector of values. It's size must be in NumberOfArguments() interval.
+ */
+ virtual void ParseValue(const std::vector<std::string> &value) = 0;
+ };
+
+ /** Base class for value-accepting flag options
+ */
+ class ValueFlagBase : public FlagBase
+ {
+ public:
+ ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {}
+ ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {}
+ virtual ~ValueFlagBase() {}
+
+ virtual Nargs NumberOfArguments() const noexcept override
+ {
+ return 1;
+ }
+ };
+
+ class CompletionFlag : public ValueFlagBase
+ {
+ public:
+ std::vector<std::string> reply;
+ size_t cword = 0;
+ std::string syntax;
+
+ template <typename GroupClass>
+ CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden)
+ {
+ group_.AddCompletion(*this);
+ }
+
+ virtual ~CompletionFlag() {}
+
+ virtual Nargs NumberOfArguments() const noexcept override
+ {
+ return 2;
+ }
+
+ virtual void ParseValue(const std::vector<std::string> &value_) override
+ {
+ syntax = value_.at(0);
+ std::istringstream(value_.at(1)) >> cword;
+ }
+
+ /** Get the completion reply
+ */
+ std::string Get() noexcept
+ {
+ return detail::Join(reply, "\n");
+ }
+
+ virtual void Reset() noexcept override
+ {
+ ValueFlagBase::Reset();
+ cword = 0;
+ syntax.clear();
+ reply.clear();
+ }
+ };
+
+
+ /** Base class for positional options
+ */
+ class PositionalBase : public NamedBase
+ {
+ protected:
+ bool ready;
+
+ public:
+ PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {}
+ virtual ~PositionalBase() {}
+
+ bool Ready()
+ {
+ return ready;
+ }
+
+ virtual void ParseValue(const std::string &value_) = 0;
+
+ virtual void Reset() noexcept override
+ {
+ matched = false;
+ ready = true;
+#ifdef ARGS_NOEXCEPT
+ error = Error::None;
+ errorMsg.clear();
+#endif
+ }
+
+ virtual PositionalBase *GetNextPositional() override
+ {
+ return Ready() ? this : nullptr;
+ }
+
+ virtual bool HasPositional() const override
+ {
+ return true;
+ }
+
+ virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
+ {
+ return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose
+ : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose };
+ }
+
+ virtual void Validate(const std::string &, const std::string &) const override
+ {
+ if (IsRequired() && !Matched())
+ {
+ std::ostringstream problem;
+ problem << "Option '" << Name() << "' is required";
+#ifdef ARGS_NOEXCEPT
+ error = Error::Required;
+ errorMsg = problem.str();
+#else
+ throw RequiredError(problem.str());
+#endif
+ }
+ }
+ };
+
+ /** Class for all kinds of validating groups, including ArgumentParser
+ */
+ class Group : public Base
+ {
+ private:
+ std::vector<Base*> children;
+ std::function<bool(const Group &)> validator;
+
+ public:
+ /** Default validators
+ */
+ struct Validators
+ {
+ static bool Xor(const Group &group)
+ {
+ return group.MatchedChildren() == 1;
+ }
+
+ static bool AtLeastOne(const Group &group)
+ {
+ return group.MatchedChildren() >= 1;
+ }
+
+ static bool AtMostOne(const Group &group)
+ {
+ return group.MatchedChildren() <= 1;
+ }
+
+ static bool All(const Group &group)
+ {
+ return group.Children().size() == group.MatchedChildren();
+ }
+
+ static bool AllOrNone(const Group &group)
+ {
+ return (All(group) || None(group));
+ }
+
+ static bool AllChildGroups(const Group &group)
+ {
+ return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool {
+ return child->IsGroup() && !child->Matched();
+ });
+ }
+
+ static bool DontCare(const Group &)
+ {
+ return true;
+ }
+
+ static bool CareTooMuch(const Group &)
+ {
+ return false;
+ }
+
+ static bool None(const Group &group)
+ {
+ return group.MatchedChildren() == 0;
+ }
+ };
+ /// If help is empty, this group will not be printed in help output
+ Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) {}
+ /// If help is empty, this group will not be printed in help output
+ Group(Group &group_, const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_)
+ {
+ group_.Add(*this);
+ }
+ virtual ~Group() {}
+
+ /** Append a child to this Group.
+ */
+ void Add(Base &child)
+ {
+ children.emplace_back(&child);
+ }
+
+ /** Get all this group's children
+ */
+ const std::vector<Base *> &Children() const
+ {
+ return children;
+ }
+
+ /** Return the first FlagBase that matches flag, or nullptr
+ *
+ * \param flag The flag with prefixes stripped
+ * \return the first matching FlagBase pointer, or nullptr if there is no match
+ */
+ virtual FlagBase *Match(const EitherFlag &flag) override
+ {
+ for (Base *child: Children())
+ {
+ if (FlagBase *match = child->Match(flag))
+ {
+ return match;
+ }
+ }
+ return nullptr;
+ }
+
+ virtual std::vector<FlagBase*> GetAllFlags() override
+ {
+ std::vector<FlagBase*> res;
+ for (Base *child: Children())
+ {
+ auto childRes = child->GetAllFlags();
+ res.insert(res.end(), childRes.begin(), childRes.end());
+ }
+ return res;
+ }
+
+ virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
+ {
+ for (Base *child: Children())
+ {
+ child->Validate(shortPrefix, longPrefix);
+ }
+ }
+
+ /** Get the next ready positional, or nullptr if there is none
+ *
+ * \return the first ready PositionalBase pointer, or nullptr if there is no match
+ */
+ virtual PositionalBase *GetNextPositional() override
+ {
+ for (Base *child: Children())
+ {
+ if (auto next = child->GetNextPositional())
+ {
+ return next;
+ }
+ }
+ return nullptr;
+ }
+
+ /** Get whether this has any FlagBase children
+ *
+ * \return Whether or not there are any FlagBase children
+ */
+ virtual bool HasFlag() const override
+ {
+ return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); });
+ }
+
+ /** Get whether this has any PositionalBase children
+ *
+ * \return Whether or not there are any PositionalBase children
+ */
+ virtual bool HasPositional() const override
+ {
+ return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); });
+ }
+
+ /** Get whether this has any Command children
+ *
+ * \return Whether or not there are any Command children
+ */
+ virtual bool HasCommand() const override
+ {
+ return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); });
+ }
+
+ /** Count the number of matched children this group has
+ */
+ std::vector<Base *>::size_type MatchedChildren() const
+ {
+ // Cast to avoid warnings from -Wsign-conversion
+ return static_cast<std::vector<Base *>::size_type>(
+ std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();}));
+ }
+
+ /** Whether or not this group matches validation
+ */
+ virtual bool Matched() const noexcept override
+ {
+ return validator(*this);
+ }
+
+ /** Get validation
+ */
+ bool Get() const
+ {
+ return Matched();
+ }
+
+ /** Get all the child descriptions for help generation
+ */
+ virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
+ {
+ std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions;
+
+ // Push that group description on the back if not empty
+ unsigned addindent = 0;
+ if (!help.empty())
+ {
+ descriptions.emplace_back(help, "", indent);
+ addindent = 1;
+ }
+
+ for (Base *child: Children())
+ {
+ if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
+ {
+ continue;
+ }
+
+ auto groupDescriptions = child->GetDescription(params, indent + addindent);
+ descriptions.insert(
+ std::end(descriptions),
+ std::make_move_iterator(std::begin(groupDescriptions)),
+ std::make_move_iterator(std::end(groupDescriptions)));
+ }
+ return descriptions;
+ }
+
+ /** Get the names of positional parameters
+ */
+ virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
+ {
+ std::vector <std::string> names;
+ for (Base *child: Children())
+ {
+ if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None)
+ {
+ continue;
+ }
+
+ auto groupNames = child->GetProgramLine(params);
+ names.insert(
+ std::end(names),
+ std::make_move_iterator(std::begin(groupNames)),
+ std::make_move_iterator(std::end(groupNames)));
+ }
+ return names;
+ }
+
+ virtual std::vector<Command*> GetCommands() override
+ {
+ std::vector<Command*> res;
+ for (const auto &child : Children())
+ {
+ auto subparsers = child->GetCommands();
+ res.insert(std::end(res), std::begin(subparsers), std::end(subparsers));
+ }
+ return res;
+ }
+
+ virtual bool IsGroup() const override
+ {
+ return true;
+ }
+
+ virtual void Reset() noexcept override
+ {
+ Base::Reset();
+
+ for (auto &child: Children())
+ {
+ child->Reset();
+ }
+#ifdef ARGS_NOEXCEPT
+ error = Error::None;
+ errorMsg.clear();
+#endif
+ }
+
+#ifdef ARGS_NOEXCEPT
+ /// Only for ARGS_NOEXCEPT
+ virtual Error GetError() const override
+ {
+ if (error != Error::None)
+ {
+ return error;
+ }
+
+ auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
+ if (it == Children().end())
+ {
+ return Error::None;
+ } else
+ {
+ return (*it)->GetError();
+ }
+ }
+#endif
+
+ };
+
+ /** Class for using global options in ArgumentParser.
+ */
+ class GlobalOptions : public Group
+ {
+ public:
+ GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global)
+ {
+ Add(options_);
+ }
+ };
+
+ /** Utility class for building subparsers with coroutines/callbacks.
+ *
+ * Brief example:
+ * \code
+ * Command command(argumentParser, "command", "my command", [](args::Subparser &s)
+ * {
+ * // your command flags/positionals
+ * s.Parse(); //required
+ * //your command code
+ * });
+ * \endcode
+ *
+ * For ARGS_NOEXCEPT mode don't forget to check `s.GetError()` after `s.Parse()`
+ * and return if it isn't equals to args::Error::None.
+ *
+ * \sa Command
+ */
+ class Subparser : public Group
+ {
+ private:
+ std::vector<std::string> args;
+ std::vector<std::string> kicked;
+ ArgumentParser *parser = nullptr;
+ const HelpParams &helpParams;
+ const Command &command;
+ bool isParsed = false;
+
+ public:
+ Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_)
+ : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_)
+ {
+ }
+
+ Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_)
+ {
+ }
+
+ Subparser(const Subparser&) = delete;
+ Subparser(Subparser&&) = delete;
+ Subparser &operator = (const Subparser&) = delete;
+ Subparser &operator = (Subparser&&) = delete;
+
+ const Command &GetCommand()
+ {
+ return command;
+ }
+
+ /** (INTERNAL) Determines whether Parse was called or not.
+ */
+ bool IsParsed() const
+ {
+ return isParsed;
+ }
+
+ /** Continue parsing arguments for new command.
+ */
+ void Parse();
+
+ /** Returns a vector of kicked out arguments.
+ *
+ * \sa Base::KickOut
+ */
+ const std::vector<std::string> &KickedOut() const noexcept
+ {
+ return kicked;
+ }
+ };
+
+ /** Main class for building subparsers.
+ *
+ * /sa Subparser
+ */
+ class Command : public Group
+ {
+ private:
+ friend class Subparser;
+
+ std::string name;
+ std::string help;
+ std::string description;
+ std::string epilog;
+ std::string proglinePostfix;
+
+ std::function<void(Subparser&)> parserCoroutine;
+ bool commandIsRequired = true;
+ Command *selectedCommand = nullptr;
+
+ mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription;
+ mutable std::vector<std::string> subparserProgramLine;
+ mutable bool subparserHasFlag = false;
+ mutable bool subparserHasPositional = false;
+ mutable bool subparserHasCommand = false;
+#ifdef ARGS_NOEXCEPT
+ mutable Error subparserError = Error::None;
+#endif
+ mutable Subparser *subparser = nullptr;
+
+ protected:
+
+ class RaiiSubparser
+ {
+ public:
+ RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_);
+ RaiiSubparser(const Command &command_, const HelpParams &params_);
+
+ ~RaiiSubparser()
+ {
+ command.subparser = oldSubparser;
+ }
+
+ Subparser &Parser()
+ {
+ return parser;
+ }
+
+ private:
+ const Command &command;
+ Subparser parser;
+ Subparser *oldSubparser;
+ };
+
+ Command() = default;
+
+ std::function<void(Subparser&)> &GetCoroutine()
+ {
+ return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine;
+ }
+
+ Command &SelectedCommand()
+ {
+ Command *res = this;
+ while (res->selectedCommand != nullptr)
+ {
+ res = res->selectedCommand;
+ }
+
+ return *res;
+ }
+
+ const Command &SelectedCommand() const
+ {
+ const Command *res = this;
+ while (res->selectedCommand != nullptr)
+ {
+ res = res->selectedCommand;
+ }
+
+ return *res;
+ }
+
+ void UpdateSubparserHelp(const HelpParams &params) const
+ {
+ if (parserCoroutine)
+ {
+ RaiiSubparser coro(*this, params);
+#ifndef ARGS_NOEXCEPT
+ try
+ {
+ parserCoroutine(coro.Parser());
+ }
+ catch (args::SubparserError&)
+ {
+ }
+#else
+ parserCoroutine(coro.Parser());
+#endif
+ }
+ }
+
+ public:
+ Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {})
+ : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_))
+ {
+ base_.Add(*this);
+ }
+
+ /** The description that appears on the prog line after options
+ */
+ const std::string &ProglinePostfix() const
+ { return proglinePostfix; }
+
+ /** The description that appears on the prog line after options
+ */
+ void ProglinePostfix(const std::string &proglinePostfix_)
+ { this->proglinePostfix = proglinePostfix_; }
+
+ /** The description that appears above options
+ */
+ const std::string &Description() const
+ { return description; }
+ /** The description that appears above options
+ */
+
+ void Description(const std::string &description_)
+ { this->description = description_; }
+
+ /** The description that appears below options
+ */
+ const std::string &Epilog() const
+ { return epilog; }
+
+ /** The description that appears below options
+ */
+ void Epilog(const std::string &epilog_)
+ { this->epilog = epilog_; }
+
+ /** The name of command
+ */
+ const std::string &Name() const
+ { return name; }
+
+ /** The description of command
+ */
+ const std::string &Help() const
+ { return help; }
+
+ /** If value is true, parser will fail if no command was parsed.
+ *
+ * Default: true.
+ */
+ void RequireCommand(bool value)
+ { commandIsRequired = value; }
+
+ virtual bool IsGroup() const override
+ { return false; }
+
+ virtual bool Matched() const noexcept override
+ { return Base::Matched(); }
+
+ operator bool() const noexcept
+ { return Matched(); }
+
+ void Match() noexcept
+ { matched = true; }
+
+ void SelectCommand(Command *c) noexcept
+ {
+ selectedCommand = c;
+
+ if (c != nullptr)
+ {
+ c->Match();
+ }
+ }
+
+ virtual FlagBase *Match(const EitherFlag &flag) override
+ {
+ if (selectedCommand != nullptr)
+ {
+ if (auto *res = selectedCommand->Match(flag))
+ {
+ return res;
+ }
+
+ for (auto *child: Children())
+ {
+ if ((child->GetOptions() & Options::Global) != Options::None)
+ {
+ if (auto *res = child->Match(flag))
+ {
+ return res;
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ if (subparser != nullptr)
+ {
+ return subparser->Match(flag);
+ }
+
+ return Matched() ? Group::Match(flag) : nullptr;
+ }
+
+ virtual std::vector<FlagBase*> GetAllFlags() override
+ {
+ std::vector<FlagBase*> res;
+
+ if (!Matched())
+ {
+ return res;
+ }
+
+ for (auto *child: Children())
+ {
+ if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None)
+ {
+ auto childFlags = child->GetAllFlags();
+ res.insert(res.end(), childFlags.begin(), childFlags.end());
+ }
+ }
+
+ if (selectedCommand != nullptr)
+ {
+ auto childFlags = selectedCommand->GetAllFlags();
+ res.insert(res.end(), childFlags.begin(), childFlags.end());
+ }
+
+ if (subparser != nullptr)
+ {
+ auto childFlags = subparser->GetAllFlags();
+ res.insert(res.end(), childFlags.begin(), childFlags.end());
+ }
+
+ return res;
+ }
+
+ virtual PositionalBase *GetNextPositional() override
+ {
+ if (selectedCommand != nullptr)
+ {
+ if (auto *res = selectedCommand->GetNextPositional())
+ {
+ return res;
+ }
+
+ for (auto *child: Children())
+ {
+ if ((child->GetOptions() & Options::Global) != Options::None)
+ {
+ if (auto *res = child->GetNextPositional())
+ {
+ return res;
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ if (subparser != nullptr)
+ {
+ return subparser->GetNextPositional();
+ }
+
+ return Matched() ? Group::GetNextPositional() : nullptr;
+ }
+
+ virtual bool HasFlag() const override
+ {
+ return subparserHasFlag || Group::HasFlag();
+ }
+
+ virtual bool HasPositional() const override
+ {
+ return subparserHasPositional || Group::HasPositional();
+ }
+
+ virtual bool HasCommand() const override
+ {
+ return true;
+ }
+
+ std::vector<std::string> GetCommandProgramLine(const HelpParams &params) const
+ {
+ UpdateSubparserHelp(params);
+
+ auto res = Group::GetProgramLine(params);
+ res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end());
+
+ if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand))
+ {
+ res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]");
+ }
+
+ if (!Name().empty())
+ {
+ res.insert(res.begin(), Name());
+ }
+
+ if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags)
+ {
+ res.push_back(params.proglineOptions);
+ }
+
+ if (!ProglinePostfix().empty())
+ {
+ std::string line;
+ for (auto c : ProglinePostfix())
+ {
+ if (std::isspace(static_cast<unsigned char>(c)))
+ {
+ if (!line.empty())
+ {
+ res.push_back(line);
+ line.clear();
+ }
+
+ if (c == '\n')
+ {
+ res.push_back("\n");
+ }
+ }
+ else
+ {
+ line += c;
+ }
+ }
+
+ if (!line.empty())
+ {
+ res.push_back(line);
+ }
+ }
+
+ return res;
+ }
+
+ virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
+ {
+ if (!Matched())
+ {
+ return {};
+ }
+
+ return GetCommandProgramLine(params);
+ }
+
+ virtual std::vector<Command*> GetCommands() override
+ {
+ if (selectedCommand != nullptr)
+ {
+ return selectedCommand->GetCommands();
+ }
+
+ if (Matched())
+ {
+ return Group::GetCommands();
+ }
+
+ return { this };
+ }
+
+ virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
+ {
+ std::vector<std::tuple<std::string, std::string, unsigned>> descriptions;
+ unsigned addindent = 0;
+
+ UpdateSubparserHelp(params);
+
+ if (!Matched())
+ {
+ if (params.showCommandFullHelp)
+ {
+ std::ostringstream s;
+ bool empty = true;
+ for (const auto &progline: GetCommandProgramLine(params))
+ {
+ if (!empty)
+ {
+ s << ' ';
+ }
+ else
+ {
+ empty = false;
+ }
+
+ s << progline;
+ }
+
+ descriptions.emplace_back(s.str(), "", indent);
+ }
+ else
+ {
+ descriptions.emplace_back(Name(), help, indent);
+ }
+
+ if (!params.showCommandChildren && !params.showCommandFullHelp)
+ {
+ return descriptions;
+ }
+
+ addindent = 1;
+ }
+
+ if (params.showCommandFullHelp && !Matched())
+ {
+ descriptions.emplace_back("", "", indent + addindent);
+ descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent);
+ descriptions.emplace_back("", "", indent + addindent);
+ }
+
+ for (Base *child: Children())
+ {
+ if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
+ {
+ continue;
+ }
+
+ auto groupDescriptions = child->GetDescription(params, indent + addindent);
+ descriptions.insert(
+ std::end(descriptions),
+ std::make_move_iterator(std::begin(groupDescriptions)),
+ std::make_move_iterator(std::end(groupDescriptions)));
+ }
+
+ for (auto childDescription: subparserDescription)
+ {
+ std::get<2>(childDescription) += indent + addindent;
+ descriptions.push_back(std::move(childDescription));
+ }
+
+ if (params.showCommandFullHelp && !Matched())
+ {
+ descriptions.emplace_back("", "", indent + addindent);
+ if (!Epilog().empty())
+ {
+ descriptions.emplace_back(Epilog(), "", indent + addindent);
+ descriptions.emplace_back("", "", indent + addindent);
+ }
+ }
+
+ return descriptions;
+ }
+
+ virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override
+ {
+ if (!Matched())
+ {
+ return;
+ }
+
+ auto onValidationError = [&]
+ {
+ std::ostringstream problem;
+ problem << "Group validation failed somewhere!";
+#ifdef ARGS_NOEXCEPT
+ error = Error::Validation;
+ errorMsg = problem.str();
+#else
+ throw ValidationError(problem.str());
+#endif
+ };
+
+ for (Base *child: Children())
+ {
+ if (child->IsGroup() && !child->Matched())
+ {
+ onValidationError();
+ }
+
+ child->Validate(shortprefix, longprefix);
+ }
+
+ if (subparser != nullptr)
+ {
+ subparser->Validate(shortprefix, longprefix);
+ if (!subparser->Matched())
+ {
+ onValidationError();
+ }
+ }
+
+ if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand))
+ {
+ std::ostringstream problem;
+ problem << "Command is required";
+#ifdef ARGS_NOEXCEPT
+ error = Error::Validation;
+ errorMsg = problem.str();
+#else
+ throw ValidationError(problem.str());
+#endif
+ }
+ }
+
+ virtual void Reset() noexcept override
+ {
+ Group::Reset();
+ selectedCommand = nullptr;
+ subparserProgramLine.clear();
+ subparserDescription.clear();
+ subparserHasFlag = false;
+ subparserHasPositional = false;
+ subparserHasCommand = false;
+#ifdef ARGS_NOEXCEPT
+ subparserError = Error::None;
+#endif
+ }
+
+#ifdef ARGS_NOEXCEPT
+ /// Only for ARGS_NOEXCEPT
+ virtual Error GetError() const override
+ {
+ if (!Matched())
+ {
+ return Error::None;
+ }
+
+ if (error != Error::None)
+ {
+ return error;
+ }
+
+ if (subparserError != Error::None)
+ {
+ return subparserError;
+ }
+
+ return Group::GetError();
+ }
+#endif
+ };
+
+ /** The main user facing command line argument parser class
+ */
+ class ArgumentParser : public Command
+ {
+ friend class Subparser;
+
+ private:
+ std::string longprefix;
+ std::string shortprefix;
+
+ std::string longseparator;
+
+ std::string terminator;
+
+ bool allowJoinedShortValue = true;
+ bool allowJoinedLongValue = true;
+ bool allowSeparateShortValue = true;
+ bool allowSeparateLongValue = true;
+
+ CompletionFlag *completion = nullptr;
+ bool readCompletion = false;
+
+ protected:
+ enum class OptionType
+ {
+ LongFlag,
+ ShortFlag,
+ Positional
+ };
+
+ OptionType ParseOption(const std::string &s, bool allowEmpty = false)
+ {
+ if (s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length()))
+ {
+ return OptionType::LongFlag;
+ }
+
+ if (s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length()))
+ {
+ return OptionType::ShortFlag;
+ }
+
+ return OptionType::Positional;
+ }
+
+ template <typename It>
+ bool Complete(FlagBase &flag, It it, It end)
+ {
+ auto nextIt = it;
+ if (!readCompletion || (++nextIt != end))
+ {
+ return false;
+ }
+
+ const auto &chunk = *it;
+ for (auto &choice : flag.HelpChoices(helpParams))
+ {
+ AddCompletionReply(chunk, choice);
+ }
+
+#ifndef ARGS_NOEXCEPT
+ throw Completion(completion->Get());
+#else
+ return true;
+#endif
+ }
+
+ /** (INTERNAL) Parse flag's values
+ *
+ * \param arg The string to display in error message as a flag name
+ * \param[in, out] it The iterator to first value. It will point to the last value
+ * \param end The end iterator
+ * \param joinedArg Joined value (e.g. bar in --foo=bar)
+ * \param canDiscardJoined If true joined value can be parsed as flag not as a value (as in -abcd)
+ * \param[out] values The vector to store parsed arg's values
+ */
+ template <typename It>
+ std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end,
+ const bool allowSeparate, const bool allowJoined,
+ const bool hasJoined, const std::string &joinedArg,
+ const bool canDiscardJoined, std::vector<std::string> &values)
+ {
+ values.clear();
+
+ Nargs nargs = flag.NumberOfArguments();
+
+ if (hasJoined && !allowJoined && nargs.min != 0)
+ {
+ return "Flag '" + arg + "' was passed a joined argument, but these are disallowed";
+ }
+
+ if (hasJoined)
+ {
+ if (!canDiscardJoined || nargs.max != 0)
+ {
+ values.push_back(joinedArg);
+ }
+ } else if (!allowSeparate)
+ {
+ if (nargs.min != 0)
+ {
+ return "Flag '" + arg + "' was passed a separate argument, but these are disallowed";
+ }
+ } else
+ {
+ auto valueIt = it;
+ ++valueIt;
+
+ while (valueIt != end &&
+ values.size() < nargs.max &&
+ (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional))
+ {
+ if (Complete(flag, valueIt, end))
+ {
+ it = end;
+ return "";
+ }
+
+ values.push_back(*valueIt);
+ ++it;
+ ++valueIt;
+ }
+ }
+
+ if (values.size() > nargs.max)
+ {
+ return "Passed an argument into a non-argument flag: " + arg;
+ } else if (values.size() < nargs.min)
+ {
+ if (nargs.min == 1 && nargs.max == 1)
+ {
+ return "Flag '" + arg + "' requires an argument but received none";
+ } else if (nargs.min == 1)
+ {
+ return "Flag '" + arg + "' requires at least one argument but received none";
+ } else if (nargs.min != nargs.max)
+ {
+ return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) +
+ " arguments but received " + std::to_string(values.size());
+ } else
+ {
+ return "Flag '" + arg + "' requires " + std::to_string(nargs.min) +
+ " arguments but received " + std::to_string(values.size());
+ }
+ }
+
+ return {};
+ }
+
+ template <typename It>
+ bool ParseLong(It &it, It end)
+ {
+ const auto &chunk = *it;
+ const auto argchunk = chunk.substr(longprefix.size());
+ // Try to separate it, in case of a separator:
+ const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator);
+ // If the separator is in the argument, separate it.
+ const auto arg = (separator != argchunk.npos ?
+ std::string(argchunk, 0, separator)
+ : argchunk);
+ const auto joined = (separator != argchunk.npos ?
+ argchunk.substr(separator + longseparator.size())
+ : std::string());
+
+ if (auto flag = Match(arg))
+ {
+ std::vector<std::string> values;
+ const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue,
+ separator != argchunk.npos, joined, false, values);
+ if (!errorMessage.empty())
+ {
+#ifndef ARGS_NOEXCEPT
+ throw ParseError(errorMessage);
+#else
+ error = Error::Parse;
+ errorMsg = errorMessage;
+ return false;
+#endif
+ }
+
+ if (!readCompletion)
+ {
+ flag->ParseValue(values);
+ }
+
+ if (flag->KickOut())
+ {
+ ++it;
+ return false;
+ }
+ } else
+ {
+ const std::string errorMessage("Flag could not be matched: " + arg);
+#ifndef ARGS_NOEXCEPT
+ throw ParseError(errorMessage);
+#else
+ error = Error::Parse;
+ errorMsg = errorMessage;
+ return false;
+#endif
+ }
+
+ return true;
+ }
+
+ template <typename It>
+ bool ParseShort(It &it, It end)
+ {
+ const auto &chunk = *it;
+ const auto argchunk = chunk.substr(shortprefix.size());
+ for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit)
+ {
+ const auto arg = *argit;
+
+ if (auto flag = Match(arg))
+ {
+ const std::string value(argit + 1, std::end(argchunk));
+ std::vector<std::string> values;
+ const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end,
+ allowSeparateShortValue, allowJoinedShortValue,
+ !value.empty(), value, !value.empty(), values);
+
+ if (!errorMessage.empty())
+ {
+#ifndef ARGS_NOEXCEPT
+ throw ParseError(errorMessage);
+#else
+ error = Error::Parse;
+ errorMsg = errorMessage;
+ return false;
+#endif
+ }
+
+ if (!readCompletion)
+ {
+ flag->ParseValue(values);
+ }
+
+ if (flag->KickOut())
+ {
+ ++it;
+ return false;
+ }
+
+ if (!values.empty())
+ {
+ break;
+ }
+ } else
+ {
+ const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'");
+#ifndef ARGS_NOEXCEPT
+ throw ParseError(errorMessage);
+#else
+ error = Error::Parse;
+ errorMsg = errorMessage;
+ return false;
+#endif
+ }
+ }
+
+ return true;
+ }
+
+ bool AddCompletionReply(const std::string &cur, const std::string &choice)
+ {
+ if (cur.empty() || choice.find(cur) == 0)
+ {
+ if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos)
+ {
+ completion->reply.push_back(choice.substr(choice.find(longseparator) + 1));
+ } else
+ {
+ completion->reply.push_back(choice);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ template <typename It>
+ bool Complete(It it, It end)
+ {
+ auto nextIt = it;
+ if (!readCompletion || (++nextIt != end))
+ {
+ return false;
+ }
+
+ const auto &chunk = *it;
+ auto pos = GetNextPositional();
+ std::vector<Command *> commands = GetCommands();
+ const auto optionType = ParseOption(chunk, true);
+
+ if (!commands.empty() && (chunk.empty() || optionType == OptionType::Positional))
+ {
+ for (auto &cmd : commands)
+ {
+ if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
+ {
+ AddCompletionReply(chunk, cmd->Name());
+ }
+ }
+ } else
+ {
+ bool hasPositionalCompletion = true;
+
+ if (!commands.empty())
+ {
+ for (auto &cmd : commands)
+ {
+ if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
+ {
+ AddCompletionReply(chunk, cmd->Name());
+ }
+ }
+ } else if (pos)
+ {
+ if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None)
+ {
+ auto choices = pos->HelpChoices(helpParams);
+ hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional;
+ for (auto &choice : choices)
+ {
+ AddCompletionReply(chunk, choice);
+ }
+ }
+ }
+
+ if (hasPositionalCompletion)
+ {
+ auto flags = GetAllFlags();
+ for (auto flag : flags)
+ {
+ if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None)
+ {
+ continue;
+ }
+
+ auto &matcher = flag->GetMatcher();
+ if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix)))
+ {
+ for (auto &flagName : matcher.GetFlagStrings())
+ {
+ if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix)))
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ if (optionType == OptionType::LongFlag && allowJoinedLongValue)
+ {
+ const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator);
+ if (separator != chunk.npos)
+ {
+ std::string arg(chunk, 0, separator);
+ if (auto flag = this->Match(arg.substr(longprefix.size())))
+ {
+ for (auto &choice : flag->HelpChoices(helpParams))
+ {
+ AddCompletionReply(chunk, arg + longseparator + choice);
+ }
+ }
+ }
+ } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue)
+ {
+ if (chunk.size() > shortprefix.size() + 1)
+ {
+ auto arg = chunk.at(shortprefix.size());
+ //TODO: support -abcVALUE where a and b take no value
+ if (auto flag = this->Match(arg))
+ {
+ for (auto &choice : flag->HelpChoices(helpParams))
+ {
+ AddCompletionReply(chunk, shortprefix + arg + choice);
+ }
+ }
+ }
+ }
+ }
+ }
+
+#ifndef ARGS_NOEXCEPT
+ throw Completion(completion->Get());
+#else
+ return true;
+#endif
+ }
+
+ template <typename It>
+ It Parse(It begin, It end)
+ {
+ bool terminated = false;
+ std::vector<Command *> commands = GetCommands();
+
+ // Check all arg chunks
+ for (auto it = begin; it != end; ++it)
+ {
+ if (Complete(it, end))
+ {
+ return end;
+ }
+
+ const auto &chunk = *it;
+
+ if (!terminated && chunk == terminator)
+ {
+ terminated = true;
+ } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag)
+ {
+ if (!ParseLong(it, end))
+ {
+ return it;
+ }
+ } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag)
+ {
+ if (!ParseShort(it, end))
+ {
+ return it;
+ }
+ } else if (!terminated && !commands.empty())
+ {
+ auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; });
+ if (itCommand == commands.end())
+ {
+ const std::string errorMessage("Unknown command: " + chunk);
+#ifndef ARGS_NOEXCEPT
+ throw ParseError(errorMessage);
+#else
+ error = Error::Parse;
+ errorMsg = errorMessage;
+ return it;
+#endif
+ }
+
+ SelectCommand(*itCommand);
+
+ if (const auto &coroutine = GetCoroutine())
+ {
+ ++it;
+ RaiiSubparser coro(*this, std::vector<std::string>(it, end));
+ coroutine(coro.Parser());
+#ifdef ARGS_NOEXCEPT
+ error = GetError();
+ if (error != Error::None)
+ {
+ return end;
+ }
+
+ if (!coro.Parser().IsParsed())
+ {
+ error = Error::Usage;
+ return end;
+ }
+#else
+ if (!coro.Parser().IsParsed())
+ {
+ throw UsageError("Subparser::Parse was not called");
+ }
+#endif
+
+ break;
+ }
+
+ commands = GetCommands();
+ } else
+ {
+ auto pos = GetNextPositional();
+ if (pos)
+ {
+ pos->ParseValue(chunk);
+
+ if (pos->KickOut())
+ {
+ return ++it;
+ }
+ } else
+ {
+ const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk);
+#ifndef ARGS_NOEXCEPT
+ throw ParseError(errorMessage);
+#else
+ error = Error::Parse;
+ errorMsg = errorMessage;
+ return it;
+#endif
+ }
+ }
+
+ if (!readCompletion && completion != nullptr && completion->Matched())
+ {
+#ifdef ARGS_NOEXCEPT
+ error = Error::Completion;
+#endif
+ readCompletion = true;
+ ++it;
+ const auto argsLeft = static_cast<size_t>(std::distance(it, end));
+ if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft)
+ {
+#ifndef ARGS_NOEXCEPT
+ throw Completion("");
+#endif
+ }
+
+ std::vector<std::string> curArgs(++it, end);
+ curArgs.resize(completion->cword);
+
+ if (completion->syntax == "bash")
+ {
+ // bash tokenizes --flag=value as --flag=value
+ for (size_t idx = 0; idx < curArgs.size(); )
+ {
+ if (idx > 0 && curArgs[idx] == "=")
+ {
+ curArgs[idx - 1] += "=";
+ // Avoid warnings from -Wsign-conversion
+ const auto signedIdx = static_cast<std::ptrdiff_t>(idx);
+ if (idx + 1 < curArgs.size())
+ {
+ curArgs[idx - 1] += curArgs[idx + 1];
+ curArgs.erase(curArgs.begin() + signedIdx, curArgs.begin() + signedIdx + 2);
+ } else
+ {
+ curArgs.erase(curArgs.begin() + signedIdx);
+ }
+ } else
+ {
+ ++idx;
+ }
+ }
+
+ }
+#ifndef ARGS_NOEXCEPT
+ try
+ {
+ Parse(curArgs.begin(), curArgs.end());
+ throw Completion("");
+ }
+ catch (Completion &)
+ {
+ throw;
+ }
+ catch (args::Error&)
+ {
+ throw Completion("");
+ }
+#else
+ return Parse(curArgs.begin(), curArgs.end());
+#endif
+ }
+ }
+
+ Validate(shortprefix, longprefix);
+ return end;
+ }
+
+ public:
+ HelpParams helpParams;
+
+ ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string())
+ {
+ Description(description_);
+ Epilog(epilog_);
+ LongPrefix("--");
+ ShortPrefix("-");
+ LongSeparator("=");
+ Terminator("--");
+ SetArgumentSeparations(true, true, true, true);
+ matched = true;
+ }
+
+ void AddCompletion(CompletionFlag &completionFlag)
+ {
+ completion = &completionFlag;
+ Add(completionFlag);
+ }
+
+ /** The program name for help generation
+ */
+ const std::string &Prog() const
+ { return helpParams.programName; }
+ /** The program name for help generation
+ */
+ void Prog(const std::string &prog_)
+ { this->helpParams.programName = prog_; }
+
+ /** The prefix for long flags
+ */
+ const std::string &LongPrefix() const
+ { return longprefix; }
+ /** The prefix for long flags
+ */
+ void LongPrefix(const std::string &longprefix_)
+ {
+ this->longprefix = longprefix_;
+ this->helpParams.longPrefix = longprefix_;
+ }
+
+ /** The prefix for short flags
+ */
+ const std::string &ShortPrefix() const
+ { return shortprefix; }
+ /** The prefix for short flags
+ */
+ void ShortPrefix(const std::string &shortprefix_)
+ {
+ this->shortprefix = shortprefix_;
+ this->helpParams.shortPrefix = shortprefix_;
+ }
+
+ /** The separator for long flags
+ */
+ const std::string &LongSeparator() const
+ { return longseparator; }
+ /** The separator for long flags
+ */
+ void LongSeparator(const std::string &longseparator_)
+ {
+ if (longseparator_.empty())
+ {
+ const std::string errorMessage("longseparator can not be set to empty");
+#ifdef ARGS_NOEXCEPT
+ error = Error::Usage;
+ errorMsg = errorMessage;
+#else
+ throw UsageError(errorMessage);
+#endif
+ } else
+ {
+ this->longseparator = longseparator_;
+ this->helpParams.longSeparator = allowJoinedLongValue ? longseparator_ : " ";
+ }
+ }
+
+ /** The terminator that forcibly separates flags from positionals
+ */
+ const std::string &Terminator() const
+ { return terminator; }
+ /** The terminator that forcibly separates flags from positionals
+ */
+ void Terminator(const std::string &terminator_)
+ { this->terminator = terminator_; }
+
+ /** Get the current argument separation parameters.
+ *
+ * See SetArgumentSeparations for details on what each one means.
+ */
+ void GetArgumentSeparations(
+ bool &allowJoinedShortValue_,
+ bool &allowJoinedLongValue_,
+ bool &allowSeparateShortValue_,
+ bool &allowSeparateLongValue_) const
+ {
+ allowJoinedShortValue_ = this->allowJoinedShortValue;
+ allowJoinedLongValue_ = this->allowJoinedLongValue;
+ allowSeparateShortValue_ = this->allowSeparateShortValue;
+ allowSeparateLongValue_ = this->allowSeparateLongValue;
+ }
+
+ /** Change allowed option separation.
+ *
+ * \param allowJoinedShortValue_ Allow a short flag that accepts an argument to be passed its argument immediately next to it (ie. in the same argv field)
+ * \param allowJoinedLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by the longseparator (ie. in the same argv field)
+ * \param allowSeparateShortValue_ Allow a short flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field)
+ * \param allowSeparateLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field)
+ */
+ void SetArgumentSeparations(
+ const bool allowJoinedShortValue_,
+ const bool allowJoinedLongValue_,
+ const bool allowSeparateShortValue_,
+ const bool allowSeparateLongValue_)
+ {
+ this->allowJoinedShortValue = allowJoinedShortValue_;
+ this->allowJoinedLongValue = allowJoinedLongValue_;
+ this->allowSeparateShortValue = allowSeparateShortValue_;
+ this->allowSeparateLongValue = allowSeparateLongValue_;
+
+ this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
+ this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " ";
+ }
+
+ /** Pass the help menu into an ostream
+ */
+ void Help(std::ostream &help_) const
+ {
+ auto &command = SelectedCommand();
+ const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description();
+ const auto description_text = Wrap(commandDescription, helpParams.width - helpParams.descriptionindent);
+ const auto epilog_text = Wrap(command.Epilog(), helpParams.width - helpParams.descriptionindent);
+
+ const bool hasoptions = command.HasFlag();
+ const bool hasarguments = command.HasPositional();
+
+ std::vector<std::string> prognameline;
+ prognameline.push_back(helpParams.usageString);
+ prognameline.push_back(Prog());
+ auto commandProgLine = command.GetProgramLine(helpParams);
+ prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end());
+
+ const auto proglines = Wrap(prognameline.begin(), prognameline.end(),
+ helpParams.width - (helpParams.progindent + helpParams.progtailindent),
+ helpParams.width - helpParams.progindent);
+ auto progit = std::begin(proglines);
+ if (progit != std::end(proglines))
+ {
+ help_ << std::string(helpParams.progindent, ' ') << *progit << '\n';
+ ++progit;
+ }
+ for (; progit != std::end(proglines); ++progit)
+ {
+ help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n';
+ }
+
+ help_ << '\n';
+
+ if (!description_text.empty())
+ {
+ for (const auto &line: description_text)
+ {
+ help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
+ }
+ help_ << "\n";
+ }
+
+ bool lastDescriptionIsNewline = false;
+
+ if (!helpParams.optionsString.empty())
+ {
+ help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n";
+ }
+
+ for (const auto &desc: command.GetDescription(helpParams, 0))
+ {
+ lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty();
+ const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent;
+ const auto flags = Wrap(std::get<0>(desc), helpParams.width - (helpParams.flagindent + helpParams.helpindent + helpParams.gutter));
+ const auto info = Wrap(std::get<1>(desc), helpParams.width - (helpParams.helpindent + groupindent));
+
+ std::string::size_type flagssize = 0;
+ for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit)
+ {
+ if (flagsit != std::begin(flags))
+ {
+ help_ << '\n';
+ }
+ help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit;
+ flagssize = Glyphs(*flagsit);
+ }
+
+ auto infoit = std::begin(info);
+ // groupindent is on both sides of this inequality, and therefore can be removed
+ if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription)
+ {
+ help_ << '\n';
+ } else
+ {
+ // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here
+ help_ << std::string(helpParams.helpindent - (helpParams.flagindent + flagssize), ' ') << *infoit << '\n';
+ ++infoit;
+ }
+ for (; infoit != std::end(info); ++infoit)
+ {
+ help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n';
+ }
+ }
+ if (hasoptions && hasarguments && helpParams.showTerminator)
+ {
+ lastDescriptionIsNewline = false;
+ for (const auto &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", helpParams.width - helpParams.flagindent))
+ {
+ help_ << std::string(helpParams.flagindent, ' ') << item << '\n';
+ }
+ }
+
+ if (!lastDescriptionIsNewline)
+ {
+ help_ << "\n";
+ }
+
+ for (const auto &line: epilog_text)
+ {
+ help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
+ }
+ }
+
+ /** Generate a help menu as a string.
+ *
+ * \return the help text as a single string
+ */
+ std::string Help() const
+ {
+ std::ostringstream help_;
+ Help(help_);
+ return help_.str();
+ }
+
+ virtual void Reset() noexcept override
+ {
+ Command::Reset();
+ matched = true;
+ readCompletion = false;
+ }
+
+ /** Parse all arguments.
+ *
+ * \param begin an iterator to the beginning of the argument list
+ * \param end an iterator to the past-the-end element of the argument list
+ * \return the iterator after the last parsed value. Only useful for kick-out
+ */
+ template <typename It>
+ It ParseArgs(It begin, It end)
+ {
+ // Reset all Matched statuses and errors
+ Reset();
+#ifdef ARGS_NOEXCEPT
+ error = GetError();
+ if (error != Error::None)
+ {
+ return end;
+ }
+#endif
+ return Parse(begin, end);
+ }
+
+ /** Parse all arguments.
+ *
+ * \param args an iterable of the arguments
+ * \return the iterator after the last parsed value. Only useful for kick-out
+ */
+ template <typename T>
+ auto ParseArgs(const T &args) -> decltype(std::begin(args))
+ {
+ return ParseArgs(std::begin(args), std::end(args));
+ }
+
+ /** Convenience function to parse the CLI from argc and argv
+ *
+ * Just assigns the program name and vectorizes arguments for passing into ParseArgs()
+ *
+ * \return whether or not all arguments were parsed. This works for detecting kick-out, but is generally useless as it can't do anything with it.
+ */
+ bool ParseCLI(const int argc, const char * const * argv)
+ {
+ if (Prog().empty())
+ {
+ Prog(argv[0]);
+ }
+ const std::vector<std::string> args(argv + 1, argv + argc);
+ return ParseArgs(args) == std::end(args);
+ }
+
+ template <typename T>
+ bool ParseCLI(const T &args)
+ {
+ return ParseArgs(args) == std::end(args);
+ }
+ };
+
+ inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_)
+ : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser)
+ {
+ command.subparser = &parser;
+ }
+
+ inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams &params_): command(command_), parser(command, params_), oldSubparser(command.subparser)
+ {
+ command.subparser = &parser;
+ }
+
+ inline void Subparser::Parse()
+ {
+ isParsed = true;
+ Reset();
+ command.subparserDescription = GetDescription(helpParams, 0);
+ command.subparserHasFlag = HasFlag();
+ command.subparserHasPositional = HasPositional();
+ command.subparserHasCommand = HasCommand();
+ command.subparserProgramLine = GetProgramLine(helpParams);
+ if (parser == nullptr)
+ {
+#ifndef ARGS_NOEXCEPT
+ throw args::SubparserError();
+#else
+ error = Error::Subparser;
+ return;
+#endif
+ }
+
+ auto it = parser->Parse(args.begin(), args.end());
+ command.Validate(parser->ShortPrefix(), parser->LongPrefix());
+ kicked.assign(it, args.end());
+
+#ifdef ARGS_NOEXCEPT
+ command.subparserError = GetError();
+#endif
+ }
+
+ inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser)
+ {
+ parser.Help(os);
+ return os;
+ }
+
+ /** Boolean argument matcher
+ */
+ class Flag : public FlagBase
+ {
+ public:
+ Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_)
+ {
+ group_.Add(*this);
+ }
+
+ Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false): Flag(group_, name_, help_, std::move(matcher_), extraError_ ? Options::Single : Options::None)
+ {
+ }
+
+ virtual ~Flag() {}
+
+ /** Get whether this was matched
+ */
+ bool Get() const
+ {
+ return Matched();
+ }
+
+ virtual Nargs NumberOfArguments() const noexcept override
+ {
+ return 0;
+ }
+
+ virtual void ParseValue(const std::vector<std::string>&) override
+ {
+ }
+ };
+
+ /** Help flag class
+ *
+ * Works like a regular flag, but throws an instance of Help when it is matched
+ */
+ class HelpFlag : public Flag
+ {
+ public:
+ HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {}
+
+ virtual ~HelpFlag() {}
+
+ virtual void ParseValue(const std::vector<std::string> &)
+ {
+#ifdef ARGS_NOEXCEPT
+ error = Error::Help;
+ errorMsg = Name();
+#else
+ throw Help(Name());
+#endif
+ }
+
+ /** Get whether this was matched
+ */
+ bool Get() const noexcept
+ {
+ return Matched();
+ }
+ };
+
+ /** A flag class that simply counts the number of times it's matched
+ */
+ class CounterFlag : public Flag
+ {
+ private:
+ const int startcount;
+ int count;
+
+ public:
+ CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}):
+ Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {}
+
+ virtual ~CounterFlag() {}
+
+ virtual FlagBase *Match(const EitherFlag &arg) override
+ {
+ auto me = FlagBase::Match(arg);
+ if (me)
+ {
+ ++count;
+ }
+ return me;
+ }
+
+ /** Get the count
+ */
+ int &Get() noexcept
+ {
+ return count;
+ }
+
+ int &operator *() noexcept {
+ return count;
+ }
+
+ const int &operator *() const noexcept {
+ return count;
+ }
+
+ virtual void Reset() noexcept override
+ {
+ FlagBase::Reset();
+ count = startcount;
+ }
+ };
+
+ /** A flag class that calls a function when it's matched
+ */
+ class ActionFlag : public FlagBase
+ {
+ private:
+ std::function<void(const std::vector<std::string> &)> action;
+ Nargs nargs;
+
+ public:
+ ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, std::function<void(const std::vector<std::string> &)> action_, Options options_ = {}):
+ FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_)
+ {
+ group_.Add(*this);
+ }
+
+ ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}):
+ FlagBase(name_, help_, std::move(matcher_), options_), nargs(1)
+ {
+ group_.Add(*this);
+ action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); };
+ }
+
+ ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}):
+ FlagBase(name_, help_, std::move(matcher_), options_), nargs(0)
+ {
+ group_.Add(*this);
+ action = [action_](const std::vector<std::string> &) { return action_(); };
+ }
+
+ virtual Nargs NumberOfArguments() const noexcept override
+ { return nargs; }
+
+ virtual void ParseValue(const std::vector<std::string> &value) override
+ { action(value); }
+ };
+
+ /** A default Reader class for argument classes
+ *
+ * If destination type is assignable to std::string it uses an assignment to std::string.
+ * Otherwise ValueReader simply uses a std::istringstream to read into the destination type, and
+ * raises a ParseError if there are any characters left.
+ */
+ struct ValueReader
+ {
+ template <typename T>
+ typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type
+ operator ()(const std::string &name, const std::string &value, T &destination)
+ {
+ std::istringstream ss(value);
+ bool failed = !(ss >> destination);
+
+ if (!failed)
+ {
+ ss >> std::ws;
+ }
+
+ if (ss.rdbuf()->in_avail() > 0 || failed)
+ {
+#ifdef ARGS_NOEXCEPT
+ (void)name;
+ return false;
+#else
+ std::ostringstream problem;
+ problem << "Argument '" << name << "' received invalid value type '" << value << "'";
+ throw ParseError(problem.str());
+#endif
+ }
+ return true;
+ }
+
+ template <typename T>
+ typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type
+ operator()(const std::string &, const std::string &value, T &destination)
+ {
+ destination = value;
+ return true;
+ }
+ };
+
+ /** An argument-accepting flag class
+ *
+ * \tparam T the type to extract the argument as
+ * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
+ */
+ template <
+ typename T,
+ typename Reader = ValueReader>
+ class ValueFlag : public ValueFlagBase
+ {
+ protected:
+ T value;
+ T defaultValue;
+
+ virtual std::string GetDefaultString(const HelpParams&) const override
+ {
+ return detail::ToString(defaultValue);
+ }
+
+ private:
+ Reader reader;
+
+ public:
+
+ ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), value(defaultValue_), defaultValue(defaultValue_)
+ {
+ group_.Add(*this);
+ }
+
+ ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), const bool extraError_ = false): ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, extraError_ ? Options::Single : Options::None)
+ {
+ }
+
+ ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_)
+ {
+ }
+
+ virtual ~ValueFlag() {}
+
+ virtual void ParseValue(const std::vector<std::string> &values_) override
+ {
+ const std::string &value_ = values_.at(0);
+
+#ifdef ARGS_NOEXCEPT
+ if (!reader(name, value_, this->value))
+ {
+ error = Error::Parse;
+ }
+#else
+ reader(name, value_, this->value);
+#endif
+ }
+
+ virtual void Reset() noexcept override
+ {
+ ValueFlagBase::Reset();
+ value = defaultValue;
+ }
+
+ /** Get the value
+ */
+ T &Get() noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ T &operator *() noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ const T &operator *() const noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ T *operator ->() noexcept
+ {
+ return &value;
+ }
+
+ /** Get the value
+ */
+ const T *operator ->() const noexcept
+ {
+ return &value;
+ }
+
+ /** Get the default value
+ */
+ const T &GetDefault() noexcept
+ {
+ return defaultValue;
+ }
+ };
+
+ /** An optional argument-accepting flag class
+ *
+ * \tparam T the type to extract the argument as
+ * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
+ */
+ template <
+ typename T,
+ typename Reader = ValueReader>
+ class ImplicitValueFlag : public ValueFlag<T, Reader>
+ {
+ protected:
+ T implicitValue;
+
+ public:
+
+ ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {})
+ : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_)
+ {
+ }
+
+ ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {})
+ : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_)
+ {
+ }
+
+ ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_)
+ : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue()
+ {
+ }
+
+ virtual ~ImplicitValueFlag() {}
+
+ virtual Nargs NumberOfArguments() const noexcept override
+ {
+ return {0, 1};
+ }
+
+ virtual void ParseValue(const std::vector<std::string> &value_) override
+ {
+ if (value_.empty())
+ {
+ this->value = implicitValue;
+ } else
+ {
+ ValueFlag<T, Reader>::ParseValue(value_);
+ }
+ }
+ };
+
+ /** A variadic arguments accepting flag class
+ *
+ * \tparam T the type to extract the argument as
+ * \tparam List the list type that houses the values
+ * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
+ */
+ template <
+ typename T,
+ template <typename...> class List = detail::vector,
+ typename Reader = ValueReader>
+ class NargsValueFlag : public FlagBase
+ {
+ protected:
+
+ List<T> values;
+ const List<T> defaultValues;
+ Nargs nargs;
+ Reader reader;
+
+ public:
+
+ typedef List<T> Container;
+ typedef T value_type;
+ typedef typename Container::allocator_type allocator_type;
+ typedef typename Container::pointer pointer;
+ typedef typename Container::const_pointer const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef typename Container::size_type size_type;
+ typedef typename Container::difference_type difference_type;
+ typedef typename Container::iterator iterator;
+ typedef typename Container::const_iterator const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {})
+ : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_)
+ {
+ group_.Add(*this);
+ }
+
+ virtual ~NargsValueFlag() {}
+
+ virtual Nargs NumberOfArguments() const noexcept override
+ {
+ return nargs;
+ }
+
+ virtual void ParseValue(const std::vector<std::string> &values_) override
+ {
+ values.clear();
+
+ for (const std::string &value : values_)
+ {
+ T v;
+#ifdef ARGS_NOEXCEPT
+ if (!reader(name, value, v))
+ {
+ error = Error::Parse;
+ }
+#else
+ reader(name, value, v);
+#endif
+ values.insert(std::end(values), v);
+ }
+ }
+
+ List<T> &Get() noexcept
+ {
+ return values;
+ }
+
+ /** Get the value
+ */
+ List<T> &operator *() noexcept
+ {
+ return values;
+ }
+
+ /** Get the values
+ */
+ const List<T> &operator *() const noexcept
+ {
+ return values;
+ }
+
+ /** Get the values
+ */
+ List<T> *operator ->() noexcept
+ {
+ return &values;
+ }
+
+ /** Get the values
+ */
+ const List<T> *operator ->() const noexcept
+ {
+ return &values;
+ }
+
+ iterator begin() noexcept
+ {
+ return values.begin();
+ }
+
+ const_iterator begin() const noexcept
+ {
+ return values.begin();
+ }
+
+ const_iterator cbegin() const noexcept
+ {
+ return values.cbegin();
+ }
+
+ iterator end() noexcept
+ {
+ return values.end();
+ }
+
+ const_iterator end() const noexcept
+ {
+ return values.end();
+ }
+
+ const_iterator cend() const noexcept
+ {
+ return values.cend();
+ }
+
+ virtual void Reset() noexcept override
+ {
+ FlagBase::Reset();
+ values = defaultValues;
+ }
+
+ virtual FlagBase *Match(const EitherFlag &arg) override
+ {
+ const bool wasMatched = Matched();
+ auto me = FlagBase::Match(arg);
+ if (me && !wasMatched)
+ {
+ values.clear();
+ }
+ return me;
+ }
+ };
+
+ /** An argument-accepting flag class that pushes the found values into a list
+ *
+ * \tparam T the type to extract the argument as
+ * \tparam List the list type that houses the values
+ * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
+ */
+ template <
+ typename T,
+ template <typename...> class List = detail::vector,
+ typename Reader = ValueReader>
+ class ValueFlagList : public ValueFlagBase
+ {
+ private:
+ using Container = List<T>;
+ Container values;
+ const Container defaultValues;
+ Reader reader;
+
+ public:
+
+ typedef T value_type;
+ typedef typename Container::allocator_type allocator_type;
+ typedef typename Container::pointer pointer;
+ typedef typename Container::const_pointer const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef typename Container::size_type size_type;
+ typedef typename Container::difference_type difference_type;
+ typedef typename Container::iterator iterator;
+ typedef typename Container::const_iterator const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}):
+ ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_)
+ {
+ group_.Add(*this);
+ }
+
+ virtual ~ValueFlagList() {}
+
+ virtual void ParseValue(const std::vector<std::string> &values_) override
+ {
+ const std::string &value_ = values_.at(0);
+
+ T v;
+#ifdef ARGS_NOEXCEPT
+ if (!reader(name, value_, v))
+ {
+ error = Error::Parse;
+ }
+#else
+ reader(name, value_, v);
+#endif
+ values.insert(std::end(values), v);
+ }
+
+ /** Get the values
+ */
+ Container &Get() noexcept
+ {
+ return values;
+ }
+
+ /** Get the value
+ */
+ Container &operator *() noexcept
+ {
+ return values;
+ }
+
+ /** Get the values
+ */
+ const Container &operator *() const noexcept
+ {
+ return values;
+ }
+
+ /** Get the values
+ */
+ Container *operator ->() noexcept
+ {
+ return &values;
+ }
+
+ /** Get the values
+ */
+ const Container *operator ->() const noexcept
+ {
+ return &values;
+ }
+
+ virtual std::string Name() const override
+ {
+ return name + std::string("...");
+ }
+
+ virtual void Reset() noexcept override
+ {
+ ValueFlagBase::Reset();
+ values = defaultValues;
+ }
+
+ virtual FlagBase *Match(const EitherFlag &arg) override
+ {
+ const bool wasMatched = Matched();
+ auto me = FlagBase::Match(arg);
+ if (me && !wasMatched)
+ {
+ values.clear();
+ }
+ return me;
+ }
+
+ iterator begin() noexcept
+ {
+ return values.begin();
+ }
+
+ const_iterator begin() const noexcept
+ {
+ return values.begin();
+ }
+
+ const_iterator cbegin() const noexcept
+ {
+ return values.cbegin();
+ }
+
+ iterator end() noexcept
+ {
+ return values.end();
+ }
+
+ const_iterator end() const noexcept
+ {
+ return values.end();
+ }
+
+ const_iterator cend() const noexcept
+ {
+ return values.cend();
+ }
+ };
+
+ /** A mapping value flag class
+ *
+ * \tparam K the type to extract the argument as
+ * \tparam T the type to store the result as
+ * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
+ * \tparam Map The Map type. Should operate like std::map or std::unordered_map
+ */
+ template <
+ typename K,
+ typename T,
+ typename Reader = ValueReader,
+ template <typename...> class Map = detail::unordered_map>
+ class MapFlag : public ValueFlagBase
+ {
+ private:
+ const Map<K, T> map;
+ T value;
+ const T defaultValue;
+ Reader reader;
+
+ protected:
+ virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
+ {
+ return detail::MapKeysToStrings(map);
+ }
+
+ public:
+
+ MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
+ {
+ group_.Add(*this);
+ }
+
+ MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_ = T(), const bool extraError_ = false): MapFlag(group_, name_, help_, std::move(matcher_), map_, defaultValue_, extraError_ ? Options::Single : Options::None)
+ {
+ }
+
+ MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, Options options_): MapFlag(group_, name_, help_, std::move(matcher_), map_, T(), options_)
+ {
+ }
+
+ virtual ~MapFlag() {}
+
+ virtual void ParseValue(const std::vector<std::string> &values_) override
+ {
+ const std::string &value_ = values_.at(0);
+
+ K key;
+#ifdef ARGS_NOEXCEPT
+ if (!reader(name, value_, key))
+ {
+ error = Error::Parse;
+ }
+#else
+ reader(name, value_, key);
+#endif
+ auto it = map.find(key);
+ if (it == std::end(map))
+ {
+ std::ostringstream problem;
+ problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
+#ifdef ARGS_NOEXCEPT
+ error = Error::Map;
+ errorMsg = problem.str();
+#else
+ throw MapError(problem.str());
+#endif
+ } else
+ {
+ this->value = it->second;
+ }
+ }
+
+ /** Get the value
+ */
+ T &Get() noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ T &operator *() noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ const T &operator *() const noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ T *operator ->() noexcept
+ {
+ return &value;
+ }
+
+ /** Get the value
+ */
+ const T *operator ->() const noexcept
+ {
+ return &value;
+ }
+
+ virtual void Reset() noexcept override
+ {
+ ValueFlagBase::Reset();
+ value = defaultValue;
+ }
+ };
+
+ /** A mapping value flag list class
+ *
+ * \tparam K the type to extract the argument as
+ * \tparam T the type to store the result as
+ * \tparam List the list type that houses the values
+ * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
+ * \tparam Map The Map type. Should operate like std::map or std::unordered_map
+ */
+ template <
+ typename K,
+ typename T,
+ template <typename...> class List = detail::vector,
+ typename Reader = ValueReader,
+ template <typename...> class Map = detail::unordered_map>
+ class MapFlagList : public ValueFlagBase
+ {
+ private:
+ using Container = List<T>;
+ const Map<K, T> map;
+ Container values;
+ const Container defaultValues;
+ Reader reader;
+
+ protected:
+ virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
+ {
+ return detail::MapKeysToStrings(map);
+ }
+
+ public:
+ typedef T value_type;
+ typedef typename Container::allocator_type allocator_type;
+ typedef typename Container::pointer pointer;
+ typedef typename Container::const_pointer const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef typename Container::size_type size_type;
+ typedef typename Container::difference_type difference_type;
+ typedef typename Container::iterator iterator;
+ typedef typename Container::const_iterator const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container()): ValueFlagBase(name_, help_, std::move(matcher_)), map(map_), values(defaultValues_), defaultValues(defaultValues_)
+ {
+ group_.Add(*this);
+ }
+
+ virtual ~MapFlagList() {}
+
+ virtual void ParseValue(const std::vector<std::string> &values_) override
+ {
+ const std::string &value = values_.at(0);
+
+ K key;
+#ifdef ARGS_NOEXCEPT
+ if (!reader(name, value, key))
+ {
+ error = Error::Parse;
+ }
+#else
+ reader(name, value, key);
+#endif
+ auto it = map.find(key);
+ if (it == std::end(map))
+ {
+ std::ostringstream problem;
+ problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
+#ifdef ARGS_NOEXCEPT
+ error = Error::Map;
+ errorMsg = problem.str();
+#else
+ throw MapError(problem.str());
+#endif
+ } else
+ {
+ this->values.emplace_back(it->second);
+ }
+ }
+
+ /** Get the value
+ */
+ Container &Get() noexcept
+ {
+ return values;
+ }
+
+ /** Get the value
+ */
+ Container &operator *() noexcept
+ {
+ return values;
+ }
+
+ /** Get the values
+ */
+ const Container &operator *() const noexcept
+ {
+ return values;
+ }
+
+ /** Get the values
+ */
+ Container *operator ->() noexcept
+ {
+ return &values;
+ }
+
+ /** Get the values
+ */
+ const Container *operator ->() const noexcept
+ {
+ return &values;
+ }
+
+ virtual std::string Name() const override
+ {
+ return name + std::string("...");
+ }
+
+ virtual void Reset() noexcept override
+ {
+ ValueFlagBase::Reset();
+ values = defaultValues;
+ }
+
+ virtual FlagBase *Match(const EitherFlag &arg) override
+ {
+ const bool wasMatched = Matched();
+ auto me = FlagBase::Match(arg);
+ if (me && !wasMatched)
+ {
+ values.clear();
+ }
+ return me;
+ }
+
+ iterator begin() noexcept
+ {
+ return values.begin();
+ }
+
+ const_iterator begin() const noexcept
+ {
+ return values.begin();
+ }
+
+ const_iterator cbegin() const noexcept
+ {
+ return values.cbegin();
+ }
+
+ iterator end() noexcept
+ {
+ return values.end();
+ }
+
+ const_iterator end() const noexcept
+ {
+ return values.end();
+ }
+
+ const_iterator cend() const noexcept
+ {
+ return values.cend();
+ }
+ };
+
+ /** A positional argument class
+ *
+ * \tparam T the type to extract the argument as
+ * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
+ */
+ template <
+ typename T,
+ typename Reader = ValueReader>
+ class Positional : public PositionalBase
+ {
+ private:
+ T value;
+ const T defaultValue;
+ Reader reader;
+ public:
+ Positional(Group &group_, const std::string &name_, const std::string &help_, const T &defaultValue_ = T(), Options options_ = {}): PositionalBase(name_, help_, options_), value(defaultValue_), defaultValue(defaultValue_)
+ {
+ group_.Add(*this);
+ }
+
+ Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_)
+ {
+ }
+
+ virtual ~Positional() {}
+
+ virtual void ParseValue(const std::string &value_) override
+ {
+#ifdef ARGS_NOEXCEPT
+ if (!reader(name, value_, this->value))
+ {
+ error = Error::Parse;
+ }
+#else
+ reader(name, value_, this->value);
+#endif
+ ready = false;
+ matched = true;
+ }
+
+ /** Get the value
+ */
+ T &Get() noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ T &operator *() noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ const T &operator *() const noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ T *operator ->() noexcept
+ {
+ return &value;
+ }
+
+ /** Get the value
+ */
+ const T *operator ->() const noexcept
+ {
+ return &value;
+ }
+
+ virtual void Reset() noexcept override
+ {
+ PositionalBase::Reset();
+ value = defaultValue;
+ }
+ };
+
+ /** A positional argument class that pushes the found values into a list
+ *
+ * \tparam T the type to extract the argument as
+ * \tparam List the list type that houses the values
+ * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
+ */
+ template <
+ typename T,
+ template <typename...> class List = detail::vector,
+ typename Reader = ValueReader>
+ class PositionalList : public PositionalBase
+ {
+ private:
+ using Container = List<T>;
+ Container values;
+ const Container defaultValues;
+ Reader reader;
+
+ public:
+ typedef T value_type;
+ typedef typename Container::allocator_type allocator_type;
+ typedef typename Container::pointer pointer;
+ typedef typename Container::const_pointer const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef typename Container::size_type size_type;
+ typedef typename Container::difference_type difference_type;
+ typedef typename Container::iterator iterator;
+ typedef typename Container::const_iterator const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ PositionalList(Group &group_, const std::string &name_, const std::string &help_, const Container &defaultValues_ = Container(), Options options_ = {}): PositionalBase(name_, help_, options_), values(defaultValues_), defaultValues(defaultValues_)
+ {
+ group_.Add(*this);
+ }
+
+ PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_)
+ {
+ }
+
+ virtual ~PositionalList() {}
+
+ virtual void ParseValue(const std::string &value_) override
+ {
+ T v;
+#ifdef ARGS_NOEXCEPT
+ if (!reader(name, value_, v))
+ {
+ error = Error::Parse;
+ }
+#else
+ reader(name, value_, v);
+#endif
+ values.insert(std::end(values), v);
+ matched = true;
+ }
+
+ virtual std::string Name() const override
+ {
+ return name + std::string("...");
+ }
+
+ /** Get the values
+ */
+ Container &Get() noexcept
+ {
+ return values;
+ }
+
+ /** Get the value
+ */
+ Container &operator *() noexcept
+ {
+ return values;
+ }
+
+ /** Get the values
+ */
+ const Container &operator *() const noexcept
+ {
+ return values;
+ }
+
+ /** Get the values
+ */
+ Container *operator ->() noexcept
+ {
+ return &values;
+ }
+
+ /** Get the values
+ */
+ const Container *operator ->() const noexcept
+ {
+ return &values;
+ }
+
+ virtual void Reset() noexcept override
+ {
+ PositionalBase::Reset();
+ values = defaultValues;
+ }
+
+ virtual PositionalBase *GetNextPositional() override
+ {
+ const bool wasMatched = Matched();
+ auto me = PositionalBase::GetNextPositional();
+ if (me && !wasMatched)
+ {
+ values.clear();
+ }
+ return me;
+ }
+
+ iterator begin() noexcept
+ {
+ return values.begin();
+ }
+
+ const_iterator begin() const noexcept
+ {
+ return values.begin();
+ }
+
+ const_iterator cbegin() const noexcept
+ {
+ return values.cbegin();
+ }
+
+ iterator end() noexcept
+ {
+ return values.end();
+ }
+
+ const_iterator end() const noexcept
+ {
+ return values.end();
+ }
+
+ const_iterator cend() const noexcept
+ {
+ return values.cend();
+ }
+ };
+
+ /** A positional argument mapping class
+ *
+ * \tparam K the type to extract the argument as
+ * \tparam T the type to store the result as
+ * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
+ * \tparam Map The Map type. Should operate like std::map or std::unordered_map
+ */
+ template <
+ typename K,
+ typename T,
+ typename Reader = ValueReader,
+ template <typename...> class Map = detail::unordered_map>
+ class MapPositional : public PositionalBase
+ {
+ private:
+ const Map<K, T> map;
+ T value;
+ const T defaultValue;
+ Reader reader;
+
+ protected:
+ virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
+ {
+ return detail::MapKeysToStrings(map);
+ }
+
+ public:
+
+ MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}):
+ PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
+ {
+ group_.Add(*this);
+ }
+
+ virtual ~MapPositional() {}
+
+ virtual void ParseValue(const std::string &value_) override
+ {
+ K key;
+#ifdef ARGS_NOEXCEPT
+ if (!reader(name, value_, key))
+ {
+ error = Error::Parse;
+ }
+#else
+ reader(name, value_, key);
+#endif
+ auto it = map.find(key);
+ if (it == std::end(map))
+ {
+ std::ostringstream problem;
+ problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
+#ifdef ARGS_NOEXCEPT
+ error = Error::Map;
+ errorMsg = problem.str();
+#else
+ throw MapError(problem.str());
+#endif
+ } else
+ {
+ this->value = it->second;
+ ready = false;
+ matched = true;
+ }
+ }
+
+ /** Get the value
+ */
+ T &Get() noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ T &operator *() noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ const T &operator *() const noexcept
+ {
+ return value;
+ }
+
+ /** Get the value
+ */
+ T *operator ->() noexcept
+ {
+ return &value;
+ }
+
+ /** Get the value
+ */
+ const T *operator ->() const noexcept
+ {
+ return &value;
+ }
+
+ virtual void Reset() noexcept override
+ {
+ PositionalBase::Reset();
+ value = defaultValue;
+ }
+ };
+
+ /** A positional argument mapping list class
+ *
+ * \tparam K the type to extract the argument as
+ * \tparam T the type to store the result as
+ * \tparam List the list type that houses the values
+ * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
+ * \tparam Map The Map type. Should operate like std::map or std::unordered_map
+ */
+ template <
+ typename K,
+ typename T,
+ template <typename...> class List = detail::vector,
+ typename Reader = ValueReader,
+ template <typename...> class Map = detail::unordered_map>
+ class MapPositionalList : public PositionalBase
+ {
+ private:
+ using Container = List<T>;
+
+ const Map<K, T> map;
+ Container values;
+ const Container defaultValues;
+ Reader reader;
+
+ protected:
+ virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
+ {
+ return detail::MapKeysToStrings(map);
+ }
+
+ public:
+ typedef T value_type;
+ typedef typename Container::allocator_type allocator_type;
+ typedef typename Container::pointer pointer;
+ typedef typename Container::const_pointer const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef typename Container::size_type size_type;
+ typedef typename Container::difference_type difference_type;
+ typedef typename Container::iterator iterator;
+ typedef typename Container::const_iterator const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
+ PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
+ {
+ group_.Add(*this);
+ }
+
+ virtual ~MapPositionalList() {}
+
+ virtual void ParseValue(const std::string &value_) override
+ {
+ K key;
+#ifdef ARGS_NOEXCEPT
+ if (!reader(name, value_, key))
+ {
+ error = Error::Parse;
+ }
+#else
+ reader(name, value_, key);
+#endif
+ auto it = map.find(key);
+ if (it == std::end(map))
+ {
+ std::ostringstream problem;
+ problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
+#ifdef ARGS_NOEXCEPT
+ error = Error::Map;
+ errorMsg = problem.str();
+#else
+ throw MapError(problem.str());
+#endif
+ } else
+ {
+ this->values.emplace_back(it->second);
+ matched = true;
+ }
+ }
+
+ /** Get the value
+ */
+ Container &Get() noexcept
+ {
+ return values;
+ }
+
+ /** Get the value
+ */
+ Container &operator *() noexcept
+ {
+ return values;
+ }
+
+ /** Get the values
+ */
+ const Container &operator *() const noexcept
+ {
+ return values;
+ }
+
+ /** Get the values
+ */
+ Container *operator ->() noexcept
+ {
+ return &values;
+ }
+
+ /** Get the values
+ */
+ const Container *operator ->() const noexcept
+ {
+ return &values;
+ }
+
+ virtual std::string Name() const override
+ {
+ return name + std::string("...");
+ }
+
+ virtual void Reset() noexcept override
+ {
+ PositionalBase::Reset();
+ values = defaultValues;
+ }
+
+ virtual PositionalBase *GetNextPositional() override
+ {
+ const bool wasMatched = Matched();
+ auto me = PositionalBase::GetNextPositional();
+ if (me && !wasMatched)
+ {
+ values.clear();
+ }
+ return me;
+ }
+
+ iterator begin() noexcept
+ {
+ return values.begin();
+ }
+
+ const_iterator begin() const noexcept
+ {
+ return values.begin();
+ }
+
+ const_iterator cbegin() const noexcept
+ {
+ return values.cbegin();
+ }
+
+ iterator end() noexcept
+ {
+ return values.end();
+ }
+
+ const_iterator end() const noexcept
+ {
+ return values.end();
+ }
+
+ const_iterator cend() const noexcept
+ {
+ return values.cend();
+ }
+ };
+}
+
+#endif
diff --git a/tb/top/conspiracion/platform.sv b/tb/top/conspiracion/platform.sv
new file mode 100644
index 0000000..bbf1088
--- /dev/null
+++ b/tb/top/conspiracion/platform.sv
@@ -0,0 +1,430 @@
+`include "cache/defs.sv"
+
+module platform
+(
+ input wire [7:0] buttons_external_connection_export, // buttons_external_connection.export
+ input wire clk_clk, // clk.clk
+ output wire [12:0] memory_mem_a, // memory.mem_a
+ output wire [2:0] memory_mem_ba, // .mem_ba
+ output wire memory_mem_ck, // .mem_ck
+ output wire memory_mem_ck_n, // .mem_ck_n
+ output wire memory_mem_cke, // .mem_cke
+ output wire memory_mem_cs_n, // .mem_cs_n
+ output wire memory_mem_ras_n, // .mem_ras_n
+ output wire memory_mem_cas_n, // .mem_cas_n
+ output wire memory_mem_we_n, // .mem_we_n
+ output wire memory_mem_reset_n, // .mem_reset_n
+ inout wire [7:0] memory_mem_dq, // .mem_dq
+ inout wire memory_mem_dqs, // .mem_dqs
+ inout wire memory_mem_dqs_n, // .mem_dqs_n
+ output wire memory_mem_odt, // .mem_odt
+ output wire memory_mem_dm, // .mem_dm
+ input wire memory_oct_rzqin, // .oct_rzqin
+ output wire [7:0] pio_0_external_connection_export, // pio_0_external_connection.export
+ input wire pll_0_reset_reset, // pll_0_reset.reset
+ input wire reset_reset_n /*verilator public*/, // reset.reset_n
+ input wire [7:0] switches_external_connection_export, // switches_external_connection.export
+ output wire sys_sdram_pll_0_sdram_clk_clk, // sys_sdram_pll_0_sdram_clk.clk
+ output wire vga_dac_CLK, // vga_dac.CLK
+ output wire vga_dac_HS, // .HS
+ output wire vga_dac_VS, // .VS
+ output wire vga_dac_BLANK, // .BLANK
+ output wire vga_dac_SYNC, // .SYNC
+ output wire [7:0] vga_dac_R, // .R
+ output wire [7:0] vga_dac_G, // .G
+ output wire [7:0] vga_dac_B, // .B
+ output wire [12:0] vram_wire_addr, // vram_wire.addr
+ output wire [1:0] vram_wire_ba, // .ba
+ output wire vram_wire_cas_n, // .cas_n
+ output wire vram_wire_cke, // .cke
+ output wire vram_wire_cs_n, // .cs_n
+ inout wire [15:0] vram_wire_dq, // .dq
+ output wire [1:0] vram_wire_dqm, // .dqm
+ output wire vram_wire_ras_n, // .ras_n
+ output wire vram_wire_we_n // .we_n
+);
+
+ logic clk, rst_n;
+ assign clk = clk_clk;
+ assign rst_n = reset_reset_n;
+
+ logic[31:0] avl_address /*verilator public*/;
+ logic avl_read /*verilator public*/;
+ logic avl_write /*verilator public*/;
+ logic avl_irq /*verilator public_flat_rw @(negedge clk_clk)*/;
+ logic[127:0] avl_readdata /*verilator public_flat_rw @(negedge clk_clk)*/;
+ logic[127:0] avl_writedata /*verilator public*/;
+ logic avl_waitrequest /*verilator public_flat_rw @(negedge clk_clk)*/;
+ logic[15:0] avl_byteenable /*verilator public*/;
+
+ logic[31:0] mem_0_address, mem_1_address, mem_2_address, mem_3_address;
+ logic mem_0_read, mem_1_read, mem_2_read, mem_3_read;
+ logic mem_0_write, mem_1_write, mem_2_write, mem_3_write;
+ logic[127:0] mem_0_readdata, mem_1_readdata, mem_2_readdata, mem_3_readdata;
+ logic[127:0] mem_0_writedata, mem_1_writedata, mem_2_writedata, mem_3_writedata;
+ logic mem_0_waitrequest, mem_1_waitrequest, mem_2_waitrequest, mem_3_waitrequest;
+ logic[15:0] mem_0_byteenable, mem_1_byteenable, mem_2_byteenable, mem_3_byteenable;
+
+ logic[31:0] cpu_0_address, cpu_1_address, cpu_2_address, cpu_3_address,
+ dbg_0_address, dbg_1_address, dbg_2_address, dbg_3_address;
+ logic cpu_0_read, cpu_1_read, cpu_2_read, cpu_3_read,
+ dbg_0_read, dbg_1_read, dbg_2_read, dbg_3_read,
+ cpu_0_write, cpu_1_write, cpu_2_write, cpu_3_write,
+ dbg_0_write, dbg_1_write, dbg_2_write, dbg_3_write,
+ cpu_0_lock, cpu_1_lock, cpu_2_lock, cpu_3_lock;
+ logic[31:0] cpu_0_readdata, cpu_1_readdata, cpu_2_readdata, cpu_3_readdata,
+ dbg_0_readdata, dbg_1_readdata, dbg_2_readdata, dbg_3_readdata;
+ logic[31:0] cpu_0_writedata, cpu_1_writedata, cpu_2_writedata, cpu_3_writedata,
+ dbg_0_writedata, dbg_1_writedata, dbg_2_writedata, dbg_3_writedata;
+ logic cpu_0_waitrequest, cpu_1_waitrequest, cpu_2_waitrequest, cpu_3_waitrequest,
+ dbg_0_waitrequest, dbg_1_waitrequest, dbg_2_waitrequest, dbg_3_waitrequest;
+ logic[1:0] cpu_0_response, cpu_1_response, cpu_2_response, cpu_3_response;
+ logic[3:0] cpu_0_byteenable, cpu_1_byteenable, cpu_2_byteenable, cpu_3_byteenable;
+
+ core cpu_0
+ (
+ .step(step_0),
+ .breakpoint(breakpoint_0),
+ .cpu_halt(halt_0),
+ .cpu_halted(cpu_halted_0),
+ .avl_address(cpu_0_address),
+ .avl_read(cpu_0_read),
+ .avl_write(cpu_0_write),
+ .avl_lock(cpu_0_lock),
+ .avl_readdata(cpu_0_readdata),
+ .avl_writedata(cpu_0_writedata),
+ .avl_waitrequest(cpu_0_waitrequest),
+ .avl_response(cpu_0_response),
+ .avl_byteenable(cpu_0_byteenable),
+ .*
+ );
+
+ core cpu_1
+ (
+ .step(step_1),
+ .breakpoint(breakpoint_1),
+ .cpu_halt(halt_1),
+ .cpu_halted(cpu_halted_1),
+ .avl_address(cpu_1_address),
+ .avl_read(cpu_1_read),
+ .avl_write(cpu_1_write),
+ .avl_lock(cpu_1_lock),
+ .avl_readdata(cpu_1_readdata),
+ .avl_writedata(cpu_1_writedata),
+ .avl_waitrequest(cpu_1_waitrequest),
+ .avl_response(cpu_1_response),
+ .avl_byteenable(cpu_1_byteenable),
+ .avl_irq(0),
+ .*
+ );
+
+ core cpu_2
+ (
+ .step(step_2),
+ .breakpoint(breakpoint_2),
+ .cpu_halt(halt_2),
+ .cpu_halted(cpu_halted_2),
+ .avl_address(cpu_2_address),
+ .avl_read(cpu_2_read),
+ .avl_write(cpu_2_write),
+ .avl_lock(cpu_2_lock),
+ .avl_readdata(cpu_2_readdata),
+ .avl_writedata(cpu_2_writedata),
+ .avl_waitrequest(cpu_2_waitrequest),
+ .avl_response(cpu_2_response),
+ .avl_byteenable(cpu_2_byteenable),
+ .avl_irq(0),
+ .*
+ );
+
+ core cpu_3
+ (
+ .step(step_3),
+ .breakpoint(breakpoint_3),
+ .cpu_halt(halt_3),
+ .cpu_halted(cpu_halted_3),
+ .avl_address(cpu_3_address),
+ .avl_read(cpu_3_read),
+ .avl_write(cpu_3_write),
+ .avl_lock(cpu_3_lock),
+ .avl_readdata(cpu_3_readdata),
+ .avl_writedata(cpu_3_writedata),
+ .avl_waitrequest(cpu_3_waitrequest),
+ .avl_response(cpu_3_response),
+ .avl_byteenable(cpu_3_byteenable),
+ .avl_irq(0),
+ .*
+ );
+
+ vga_domain vga
+ (
+ .*
+ );
+
+ ring_req data_0, data_1, data_2, data_3;
+ ring_token token_0, token_1, token_2, token_3;
+
+ logic data_valid_0, data_valid_1, data_valid_2, data_valid_3,
+ data_ready_0, data_ready_1, data_ready_2, data_ready_3,
+ token_valid_0, token_valid_1, token_valid_2, token_valid_3;
+
+ cache cache_0
+ (
+ .core_address(cpu_0_address[31:2]),
+ .core_read(cpu_0_read),
+ .core_write(cpu_0_write),
+ .core_lock(cpu_0_lock),
+ .core_writedata(cpu_0_writedata),
+ .core_byteenable(cpu_0_byteenable),
+ .core_waitrequest(cpu_0_waitrequest),
+ .core_response(cpu_0_response),
+ .core_readdata(cpu_0_readdata),
+
+ .mem_waitrequest(mem_0_waitrequest),
+ .mem_readdata(mem_0_readdata),
+ .mem_address(mem_0_address),
+ .mem_read(mem_0_read),
+ .mem_write(mem_0_write),
+ .mem_writedata(mem_0_writedata),
+ .mem_byteenable(mem_0_byteenable),
+
+ .in_data_valid(data_valid_3),
+ .in_data(data_3),
+ .in_data_ready(data_ready_0),
+
+ .out_data_valid(data_valid_0),
+ .out_data(data_0),
+ .out_data_ready(data_ready_1),
+
+ .in_token(token_3),
+ .in_token_valid(token_valid_3),
+
+ .out_token(token_0),
+ .out_token_valid(token_valid_0),
+
+ .dbg_read(dbg_0_read),
+ .dbg_write(dbg_0_write),
+ .dbg_address(dbg_0_address[2:0]),
+ .dbg_readdata(dbg_0_readdata),
+ .dbg_writedata(dbg_0_writedata),
+ .dbg_waitrequest(dbg_0_waitrequest),
+
+ .*
+ );
+
+ sim_slave smp_dbg_0
+ (
+ .read(dbg_0_read),
+ .write(dbg_0_write),
+ .address(dbg_0_address),
+ .readdata(dbg_0_readdata),
+ .writedata(dbg_0_writedata),
+ .waitrequest(dbg_0_waitrequest),
+
+ .*
+ );
+
+ cache cache_1
+ (
+ .core_address(cpu_1_address[31:2]),
+ .core_read(cpu_1_read),
+ .core_write(cpu_1_write),
+ .core_lock(cpu_1_lock),
+ .core_writedata(cpu_1_writedata),
+ .core_byteenable(cpu_1_byteenable),
+ .core_waitrequest(cpu_1_waitrequest),
+ .core_response(cpu_1_response),
+ .core_readdata(cpu_1_readdata),
+
+ .mem_waitrequest(mem_1_waitrequest),
+ .mem_readdata(mem_1_readdata),
+ .mem_address(mem_1_address),
+ .mem_read(mem_1_read),
+ .mem_write(mem_1_write),
+ .mem_writedata(mem_1_writedata),
+ .mem_byteenable(mem_1_byteenable),
+
+ .in_data_valid(data_valid_0),
+ .in_data(data_0),
+ .in_data_ready(data_ready_1),
+
+ .out_data_valid(data_valid_1),
+ .out_data(data_1),
+ .out_data_ready(data_ready_2),
+
+ .in_token(token_0),
+ .in_token_valid(token_valid_0),
+
+ .out_token(token_1),
+ .out_token_valid(token_valid_1),
+
+ .dbg_read(dbg_1_read),
+ .dbg_write(dbg_1_write),
+ .dbg_address(dbg_1_address[2:0]),
+ .dbg_readdata(dbg_1_readdata),
+ .dbg_writedata(dbg_1_writedata),
+ .dbg_waitrequest(dbg_1_waitrequest),
+
+ .*
+ );
+
+ sim_slave smp_dbg_1
+ (
+ .read(dbg_1_read),
+ .write(dbg_1_write),
+ .address(dbg_1_address),
+ .readdata(dbg_1_readdata),
+ .writedata(dbg_1_writedata),
+ .waitrequest(dbg_1_waitrequest),
+
+ .*
+ );
+
+ cache cache_2
+ (
+ .core_address(cpu_2_address[31:2]),
+ .core_read(cpu_2_read),
+ .core_write(cpu_2_write),
+ .core_lock(cpu_2_lock),
+ .core_writedata(cpu_2_writedata),
+ .core_byteenable(cpu_2_byteenable),
+ .core_waitrequest(cpu_2_waitrequest),
+ .core_response(cpu_2_response),
+ .core_readdata(cpu_2_readdata),
+
+ .mem_waitrequest(mem_2_waitrequest),
+ .mem_readdata(mem_2_readdata),
+ .mem_address(mem_2_address),
+ .mem_read(mem_2_read),
+ .mem_write(mem_2_write),
+ .mem_writedata(mem_2_writedata),
+ .mem_byteenable(mem_2_byteenable),
+
+ .in_data_valid(data_valid_1),
+ .in_data(data_1),
+ .in_data_ready(data_ready_2),
+
+ .out_data_valid(data_valid_2),
+ .out_data(data_2),
+ .out_data_ready(data_ready_3),
+
+ .in_token(token_1),
+ .in_token_valid(token_valid_1),
+
+ .out_token(token_2),
+ .out_token_valid(token_valid_2),
+
+ .dbg_read(dbg_2_read),
+ .dbg_write(dbg_2_write),
+ .dbg_address(dbg_2_address[2:0]),
+ .dbg_readdata(dbg_2_readdata),
+ .dbg_writedata(dbg_2_writedata),
+ .dbg_waitrequest(dbg_2_waitrequest),
+
+ .*
+ );
+
+ sim_slave smp_dbg_2
+ (
+ .read(dbg_2_read),
+ .write(dbg_2_write),
+ .address(dbg_2_address),
+ .readdata(dbg_2_readdata),
+ .writedata(dbg_2_writedata),
+ .waitrequest(dbg_2_waitrequest),
+
+ .*
+ );
+
+ cache #(.TOKEN_AT_RESET(1)) cache_3
+ (
+ .core_address(cpu_3_address[31:2]),
+ .core_read(cpu_3_read),
+ .core_write(cpu_3_write),
+ .core_lock(cpu_3_lock),
+ .core_writedata(cpu_3_writedata),
+ .core_byteenable(cpu_3_byteenable),
+ .core_waitrequest(cpu_3_waitrequest),
+ .core_response(cpu_3_response),
+ .core_readdata(cpu_3_readdata),
+
+ .mem_waitrequest(mem_3_waitrequest),
+ .mem_readdata(mem_3_readdata),
+ .mem_address(mem_3_address),
+ .mem_read(mem_3_read),
+ .mem_write(mem_3_write),
+ .mem_writedata(mem_3_writedata),
+ .mem_byteenable(mem_3_byteenable),
+
+ .in_data_valid(data_valid_2),
+ .in_data(data_2),
+ .in_data_ready(data_ready_3),
+
+ .out_data_valid(data_valid_3),
+ .out_data(data_3),
+ .out_data_ready(data_ready_0),
+
+ .in_token(token_2),
+ .in_token_valid(token_valid_2),
+
+ .out_token(token_3),
+ .out_token_valid(token_valid_3),
+
+ .dbg_read(dbg_3_read),
+ .dbg_write(dbg_3_write),
+ .dbg_address(dbg_3_address[2:0]),
+ .dbg_readdata(dbg_3_readdata),
+ .dbg_writedata(dbg_3_writedata),
+ .dbg_waitrequest(dbg_3_waitrequest),
+
+ .*
+ );
+
+ sim_slave smp_dbg_3
+ (
+ .read(dbg_3_read),
+ .write(dbg_3_write),
+ .address(dbg_3_address),
+ .readdata(dbg_3_readdata),
+ .writedata(dbg_3_writedata),
+ .waitrequest(dbg_3_waitrequest),
+
+ .*
+ );
+
+ word smp_readdata, smp_writedata;
+ logic smp_read, smp_write;
+
+ sim_slave smp_sim
+ (
+ .read(smp_read),
+ .write(smp_write),
+ .address(),
+ .readdata(smp_readdata),
+ .writedata(smp_writedata),
+ .waitrequest(0),
+
+ .*
+ );
+
+ logic step_0, step_1, step_2, step_3,
+ halt_0, halt_1, halt_2, halt_3,
+ breakpoint_0, breakpoint_1, breakpoint_2, breakpoint_3,
+ cpu_halted_0, cpu_halted_1, cpu_halted_2, cpu_halted_3;
+
+ smp_ctrl smp
+ (
+ .avl_read(smp_read),
+ .avl_write(smp_write),
+ .avl_writedata(smp_writedata),
+ .avl_readdata(smp_readdata),
+
+ .*
+ );
+
+ mem_interconnect mem
+ (
+ .*
+ );
+
+endmodule
diff --git a/tb/top/conspiracion/sim_slave.cpp b/tb/top/conspiracion/sim_slave.cpp
new file mode 100644
index 0000000..24528d2
--- /dev/null
+++ b/tb/top/conspiracion/sim_slave.cpp
@@ -0,0 +1,60 @@
+#include <cstdint>
+
+#include "avalon.hpp"
+#include "sim_slave.hpp"
+
+namespace taller::avalon
+{
+ sim_slave::sim_slave(verilated_slave &dev, std::uint32_t base, std::uint32_t size)
+ : slave(base, size, 4),
+ dev(dev)
+ {
+ dev.avl_read = 0;
+ dev.avl_write = 0;
+ }
+
+ void sim_slave::tick() noexcept
+ {
+ if (latch) {
+ dev.avl_read = 0;
+ dev.avl_write = 0;
+ }
+ }
+
+ void sim_slave::tick_falling() noexcept
+ {
+ if ((dev.avl_read || dev.avl_write) && !dev.avl_waitrequest) {
+ latch = true;
+ latch_readdata = dev.avl_readdata;
+ }
+ }
+
+ bool sim_slave::read(std::uint32_t addr, std::uint32_t &data)
+ {
+ if (latch) {
+ data = latch_readdata;
+
+ latch = false;
+ return true;
+ } else if (!dev.avl_read && !dev.avl_write) {
+ dev.avl_read = 1;
+ dev.avl_address = addr;
+ }
+
+ return false;
+ }
+
+ bool sim_slave::write(std::uint32_t addr, std::uint32_t data, unsigned byte_enable)
+ {
+ if (latch) {
+ latch = false;
+ return true;
+ } else if (!dev.avl_read && !dev.avl_write) {
+ dev.avl_write = 1;
+ dev.avl_address = addr;
+ dev.avl_writedata = data;
+ }
+
+ return false;
+ }
+}
diff --git a/tb/top/conspiracion/sim_slave.hpp b/tb/top/conspiracion/sim_slave.hpp
new file mode 100644
index 0000000..12cec62
--- /dev/null
+++ b/tb/top/conspiracion/sim_slave.hpp
@@ -0,0 +1,36 @@
+#ifndef TALLER_SIM_SLAVE_HPP
+#define TALLER_SIM_SLAVE_HPP
+
+#include <cstdint>
+
+#include "Vtop_sim_slave.h"
+
+#include "avalon.hpp"
+
+namespace taller::avalon
+{
+ using verilated_slave = Vtop_sim_slave;
+
+ class sim_slave : public slave
+ {
+ public:
+ sim_slave(verilated_slave &dev, std::uint32_t base, std::uint32_t size);
+
+ virtual void tick() noexcept final override;
+ virtual void tick_falling() noexcept final override;
+
+ virtual bool read(std::uint32_t addr, std::uint32_t &data) final override;
+
+ virtual bool write
+ (
+ std::uint32_t addr, std::uint32_t data, unsigned byte_enable = 0b1111
+ ) final override;
+
+ private:
+ verilated_slave &dev;
+ bool latch;
+ std::uint32_t latch_readdata;
+ };
+}
+
+#endif
diff --git a/tb/top/conspiracion/sim_slave.sv b/tb/top/conspiracion/sim_slave.sv
new file mode 100644
index 0000000..1598701
--- /dev/null
+++ b/tb/top/conspiracion/sim_slave.sv
@@ -0,0 +1,28 @@
+module sim_slave
+(
+ input logic clk,
+
+ input logic waitrequest,
+ input logic[31:0] readdata,
+ output logic[31:0] address,
+ writedata,
+ output logic read,
+ write
+);
+
+ logic[31:0] avl_address /*verilator public_flat_rw @(negedge clk)*/;
+ logic avl_read /*verilator public_flat_rw @(negedge clk)*/;
+ logic avl_write /*verilator public_flat_rw @(negedge clk)*/;
+ logic[31:0] avl_readdata /*verilator public*/;
+ logic[31:0] avl_writedata /*verilator public_flat_rw @(negedge clk)*/;
+ logic avl_waitrequest /*verilator public*/;
+
+ assign read = avl_read;
+ assign write = avl_write;
+ assign address = avl_address;
+ assign writedata = avl_writedata;
+
+ assign avl_readdata = readdata;
+ assign avl_waitrequest = waitrequest;
+
+endmodule
diff --git a/tb/top/conspiracion/vga_domain.sv b/tb/top/conspiracion/vga_domain.sv
new file mode 100644
index 0000000..0c9aac5
--- /dev/null
+++ b/tb/top/conspiracion/vga_domain.sv
@@ -0,0 +1,36 @@
+module vga_domain
+(
+ input logic clk_clk,
+ reset_reset_n /*verilator public*/
+);
+
+ logic[25:0] avl_address /*verilator public*/;
+ logic avl_read /*verilator public*/;
+ logic avl_write /*verilator public*/;
+ logic avl_irq /*verilator public_flat_rw @(negedge clk_clk)*/;
+ logic[15:0] avl_readdata /*verilator public_flat_rw @(negedge clk_clk)*/;
+ logic[31:0] avl_writedata /*verilator public*/;
+ logic avl_waitrequest /*verilator public_flat_rw @(negedge clk_clk)*/;
+ logic avl_readdatavalid;
+ logic[3:0] avl_byteenable /*verilator public*/;
+
+ assign avl_write = 0;
+ assign avl_readdatavalid = avl_read && !avl_waitrequest;
+
+ logic vga_clk /*verilator public*/;
+ logic vga_hsync /*verilator public*/;
+ logic vga_vsync /*verilator public*/;
+ logic vga_blank_n /*verilator public*/;
+ logic vga_sync_n /*verilator public*/;
+ logic[7:0] vga_r /*verilator public*/;
+ logic[7:0] vga_g /*verilator public*/;
+ logic[7:0] vga_b /*verilator public*/;
+
+ vga crtc
+ (
+ .clk(clk_clk),
+ .rst_n(reset_reset_n),
+ .*
+ );
+
+endmodule
diff --git a/tb/top/decode_test.cpp b/tb/top/decode_test.cpp
deleted file mode 100644
index 4b78535..0000000
--- a/tb/top/decode_test.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-#include <cstdio>
-
-#include <verilated.h>
-#include <verilated_vcd_c.h>
-
-#include "Vdecode_test.h" // From Verilating "top.v"
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv); // Remember args
- Verilated::traceEverOn(true);
-
- Vdecode_test top;
- VerilatedVcdC trace;
-
- top.trace(&trace, 0);
- trace.open("decode_test.vcd");
-
- // sim/control_flow.c
- uint32_t rom[] =
- {
- 0xea000032,
- 0xea000034,
- 0xea000000,
- 0xeafffffe,
- 0xeafffffe,
- 0xeafffffe,
- 0xeafffffe,
- 0xe3500000,
- 0xda000001,
- 0xe3a00001,
- 0xe1a0f00e,
- 0x13e00000,
- 0x03a00000,
- 0xe1a0f00e,
- 0xe1a02000,
- 0xe92d4010,
- 0xe1a00002,
- 0xebfffff4,
- 0xe1a03000,
- 0xe1a00001,
- 0xebfffff1,
- 0xe0933000,
- 0x0a00000c,
- 0xe3730001,
- 0x0a00000e,
- 0xe3530001,
- 0x0a00000a,
- 0xe1a038a1,
- 0xe1a00502,
- 0xe1833601,
- 0xe02023c2,
- 0xe0831001,
- 0xe1520001,
- 0xcaffffed,
- 0xe0820001,
- 0xe8bd8010,
- 0xe1a02822,
- 0xe1822801,
- 0xe1a00f62,
- 0xe8bd8010,
- 0xe3500000,
- 0xda000002,
- 0xe1a02001,
- 0xe2611001,
- 0xeafffff2,
- 0xe1e02001,
- 0xe1a00002,
- 0xebffffd6,
- 0xe3500000,
- 0xdaffffed,
- 0xe1a01002,
- 0xeafffff5,
- 0xe59fd008,
- 0xebffffd7,
- 0xeafffffe,
- 0xeafffffe,
- 0x20000000,
- };
-
- int clk_tick = 0;
- int time = 0;
-
- for(int i = 0; i < sizeof(rom)/sizeof(rom[0]); ++i)
- {
- top.insn = rom[i];
-
- top.eval();
- trace.dump(time++);
-
- std::printf("insn=0x%08x, dec=0x", top.insn);
- for(std::size_t j = 0; j < sizeof(top.dec) / sizeof(top.dec[0]); ++j)
- {
- std::printf("%08x", top.dec[j]);
- }
-
- std::puts("");
- }
-
- trace.close();
- top.final(); // Done simulating
-}
diff --git a/tb/top/fetch_test.cpp b/tb/top/fetch_test.cpp
deleted file mode 100644
index 06768c4..0000000
--- a/tb/top/fetch_test.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-#include <cstdio>
-
-#include <verilated.h>
-#include <verilated_vcd_c.h>
-
-#include "Vfetch_test.h" // From Verilating "top.v"
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv); // Remember args
- Verilated::traceEverOn(true);
-
- Vfetch_test top;
- VerilatedVcdC trace;
-
- top.trace(&trace, 0);
- trace.open("fetch_test.vcd");
-
- top.clk = 0;
- top.stall = 0; //insn y insn_pc se detienen
- top.branch = 0; //forma de flush -> instr saltan a la instr de la branch
- top.prefetch_flush = 0; //limpia prefetch
- top.fetched = 1; //estado del fetch (ready)
- top.wr_pc = 0; //cuando hay un write al pc
- top.branch_target = 0; //direccion a la que se hace salto
- top.wr_current = 0; //ultimo que se guardo en registros
- top.fetch_data = 0x00000000; //data que se leyó al hacer fetch
-
- uint32_t rom[] =
- {
- 0xea000032,
- 0xea000034,
- 0xea000000,
- 0xeafffffe,
- 0xeafffffe,
- 0xeafffffe,
- 0xeafffffe,
- 0xe3500000,
- 0xda000001,
- 0xe3a00001,
- 0xe1a0f00e,
- 0x13e00000,
- 0x03a00000,
- 0xe1a0f00e,
- 0xe1a02000,
- 0xe92d4010,
- 0xe1a00002,
- 0xebfffff4,
- 0xe1a03000,
- 0xe1a00001,
- 0xebfffff1,
- 0xe0933000,
- 0x0a00000c,
- 0xe3730001,
- 0x0a00000e,
- 0xe3530001,
- 0x0a00000a,
- 0xe1a038a1,
- 0xe1a00502,
- 0xe1833601,
- 0xe02023c2,
- 0xe0831001,
- 0xe1520001,
- 0xcaffffed,
- 0xe0820001,
- 0xe8bd8010,
- 0xe1a02822,
- 0xe1822801,
- 0xe1a00f62,
- 0xe8bd8010,
- 0xe3500000,
- 0xda000002,
- 0xe1a02001,
- 0xe2611001,
- 0xeafffff2,
- 0xe1e02001,
- 0xe1a00002,
- 0xebffffd6,
- 0xe3500000,
- 0xdaffffed,
- 0xe1a01002,
- 0xeafffff5,
- 0xe59fd008,
- 0xebffffd7,
- 0xeafffffe,
- 0xeafffffe,
- 0x20000000,
- };
-
- int clk_tick = 0;
- int time = 0;
-
- std::printf("CPU cycle:\n");
- while (time < 50)
- {
- top.eval();
- trace.dump(time++);
-
- if(!top.clk)
- {
- std::printf("insn=0x%08x, insn_pc=0x%08x, addr=0x%08x\n",
- top.insn, top.insn_pc, top.addr);
- }
-
- top.clk = !top.clk;
-
- top.fetch_data = rom[top.addr];
- }
-
- std::printf("Branch, flush, stall:\n");
- while (time < 99)
- {
- top.eval();
- trace.dump(time++);
-
- if(!top.clk)
- {
- std::printf("insn=0x%08x, insn_pc=0x%08x, addr=0x%08x\n",
- top.insn, top.insn_pc, top.addr);
- }
-
- top.clk = !top.clk;
-
- if(time == 55)
- {
- std::printf("Se hace un branch:\n");
- top.branch = 1;
- top.branch_target = 3;
- }
-
- if(time == 59)
- {
- std::printf("Se termina el branch:\n");
- top.branch = 0;
-
- }
-
- if(time == 63){
- std::printf("Se hace un flush:\n");
- top.branch = 0;
- top.branch_target = 0;
- top.prefetch_flush = 1;
- }
-
- if(time == 69){
- std::printf("Se termina el flush:\n");
- top.prefetch_flush = 0;
- }
-
- if(time == 75)
- {
- std::printf("Se hace un stall:\n");
- top.stall = 1;
- }
-
- if(time == 81)
- {
- std::printf("Se termina el stall:\n");
- top.stall = 0;
- }
-
- top.fetch_data = rom[top.addr];
- }
-
- trace.close();
- top.final(); // Done simulating
-}
diff --git a/tb/top/hps_sdram_test.cpp b/tb/top/hps_sdram_test.cpp
deleted file mode 100644
index b173c41..0000000
--- a/tb/top/hps_sdram_test.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-#include <cstdio>
-
-#include <verilated.h>
-#include <verilated_vcd_c.h>
-
-#include "Vhps_sdram_test.h"
-#include "Vhps_sdram_test_hps_sdram_test.h"
-#include "Vhps_sdram_test_platform.h"
-
-#include "../avalon.hpp"
-#include "../mem.hpp"
-
-int main(int argc, char **argv)
-{
- using namespace taller::avalon;
-
- Verilated::commandArgs(argc, argv);
-
- Vhps_sdram_test top;
-
-#ifdef TRACE
- Verilated::traceEverOn(true);
- VerilatedVcdC trace;
-
- top.trace(&trace, 0);
- trace.open("trace.vcd");
-#endif
-
- interconnect<Vhps_sdram_test_platform> avl(*top.hps_sdram_test->plat);
- mem hps_ddr3(0x0000'0000, 512 << 20);
-
- avl.attach(hps_ddr3);
-
- int time = 0;
- top.clk_clk = 1;
-
- auto tick = [&]()
- {
- top.clk_clk = !top.clk_clk;
- top.eval();
- avl.tick(top.clk_clk);
-#ifdef TRACE
- trace.dump(time++);
-#endif
- };
-
- auto cycle = [&]()
- {
- tick();
- tick();
- std::printf("[%02d] out=0x%02x, done=%d\n", time, top.out, top.done);
- };
-
- auto io = [&]()
- {
- top.io = 0;
- cycle();
- top.io = 1;
- for(int i = 0; i < 4; ++i)
- {
- cycle();
- }
- };
-
- top.dir = 1;
- top.io = 1;
- top.mov = 1;
- top.clr = 1;
-
- for(int i = 0; i < 5; ++i)
- {
- top.add = 0;
- cycle();
- top.add = 1;
- cycle();
- }
-
- io();
-
- top.clr = 0;
- cycle();
- top.clr = 1;
- cycle();
-
- top.dir = 0;
- io();
-
-#ifdef TRACE
- trace.close();
-#endif
-
- top.final();
-}
diff --git a/tb/top/mul_test.cpp b/tb/top/mul_test.cpp
deleted file mode 100644
index cd99760..0000000
--- a/tb/top/mul_test.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include <cstdio>
-
-#include <verilated.h>
-#include <verilated_vcd_c.h>
-
-#include "Vmul_test.h" // From Verilating "top.v"
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv); // Remember args
- Verilated::traceEverOn(true);
-
- Vmul_test top;
- VerilatedVcdC trace;
-
- top.trace(&trace, 0);
- trace.open("mul_test.vcd");
-
- top.a = 6;
- top.b = 5;
- top.clk = 0;
- top.rst = 0;
- top.start = 0;
- top.result = 0;
- top.rdy = 0;
- top.c_hi = 0;
- top.c_lo = 0;
-
-
- int clk_tick = 0;
- int time = 0;
-
- for(int i = 0; i < 100; ++i)
- {
- if(++clk_tick == 5)
- {
- clk_tick = 0;
- top.clk = !top.clk;
- }
-
-
- if(++clk_tick == 10)
- {
- top.rst = 1;
- }
-
- if(++clk_tick == 20)
- {
- top.start = 1;
- }
-
- if(++clk_tick == 30)
- {
- top.start = 0;
- }
-
- top.eval();
- trace.dump(time++);
-
- std::printf(" [%c%c%c%c]\n",
- top.n ? 'N' : 'n',
- top.z ? 'Z' : 'z',
- top.c ? 'C' : 'c',
- top.v ? 'V' : 'v');
-
- std::printf("a=%d, b=%d, ready=%d, result=%d, [N=%d, Z=%d]",
- top.a, top.b, top.rdy, top.result, top.n, top.z);
- }
-
- trace.close();
- top.final(); // Done simulating
-} \ No newline at end of file
diff --git a/tb/top/smp_sim.cpp b/tb/top/smp_sim.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tb/top/smp_sim.cpp
diff --git a/tb/top/smp_sim.py b/tb/top/smp_sim.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tb/top/smp_sim.py