diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 4562bcd7d..f6f02249d 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -221,6 +221,7 @@ RAW_OBJS = \ V3MergeCond.o \ V3Name.o \ V3Number.o \ + V3OptionParser.o \ V3Options.o \ V3Order.o \ V3Os.o \ diff --git a/src/V3OptionParser.cpp b/src/V3OptionParser.cpp new file mode 100644 index 000000000..dd26b0846 --- /dev/null +++ b/src/V3OptionParser.cpp @@ -0,0 +1,232 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Command line option parser +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 by Wilson Snyder. This program is free software; you +// can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL +#include "V3Global.h" +#include "V3Options.h" +#endif +#include "V3Error.h" +#include "V3OptionParser.h" +#include "V3String.h" + +//###################################################################### +// V3OptionParser::Impl +struct V3OptionParser::Impl { + // TYPES + + // Setting for isOnOffAllowed() and isPartialMatchAllowed() + enum class en : uint8_t { + NONE, // "-opt" + ONOFF, // "-opt" and "-no-opt" + VALUE // "-opt val" + }; + // Base class of actual action classes + template + class ActionBase VL_NOT_FINAL : public ActionIfs { + bool m_undocumented = false; // This option is not documented + public: + virtual bool isValueNeeded() const override final { return MODE == en::VALUE; } + virtual bool isOnOffAllowed() const override final { return MODE == en::ONOFF; } + virtual bool isPartialMatchAllowed() const override final { return ALLOW_PARTIAL_MATCH; } + virtual bool isUndocumented() const override { return m_undocumented; } + virtual void undocumented() override { m_undocumented = true; } + }; + + // Actual action classes + template class ActionSet; // "-opt" for bool-ish, "-opt val" for int and string + template class ActionOnOff; // "-opt" and "-no-opt" for bool-ish + class ActionCbCall; // Callback without argument for "-opt" + class ActionCbOnOff; // Callback for "-opt" and "-no-opt" + template class ActionCbVal; // Callback for "-opt val" + class ActionCbPartialMatch; // Callback "-O3" for "-O" + class ActionCbPartialMatchVal; // Callback "-debugi-V3Options 3" for "-debugi-" + + // MEMBERS + std::map> m_options; // All actions for option + bool m_isFinalized{false}; // Becomes after finalize() is called + VSpellCheck m_spellCheck; // Suggests closest option when not found +}; + +//###################################################################### +// Action classes in V3OptionParser::Impl + +#define V3OPTION_PARSER_DEF_ACT_CLASS(className, type, body, enType) \ + template <> class V3OptionParser::Impl::className final : public ActionBase { \ + type* m_valp; /* Pointer to a option variable*/ \ +\ + public: \ + explicit className(type* valp) \ + : m_valp(valp) {} \ + virtual void exec(const char* optp, const char* argp) override { body; } \ + } + +V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, bool, *m_valp = true, en::NONE); +#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL +V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, VOptionBool, m_valp->setTrueOrFalse(true), en::NONE); +#endif +V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, int, *m_valp = std::atoi(argp), en::VALUE); +V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, string, *m_valp = argp, en::VALUE); + +V3OPTION_PARSER_DEF_ACT_CLASS(ActionOnOff, bool, *m_valp = !hasPrefixNo(optp), en::ONOFF); +#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL +V3OPTION_PARSER_DEF_ACT_CLASS(ActionOnOff, VOptionBool, m_valp->setTrueOrFalse(!hasPrefixNo(optp)), + en::ONOFF); +#endif +#undef V3OPTION_PARSER_DEF_ACT_CLASS + +#define V3OPTION_PARSER_DEF_ACT_CB_CLASS(className, funcType, body, ...) \ + class V3OptionParser::Impl::className final : public ActionBase<__VA_ARGS__> { \ + std::function m_cb; /* Callback function */ \ +\ + public: \ + using CbType = std::function; \ + explicit className(CbType cb) \ + : m_cb(std::move(cb)) {} \ + virtual void exec(const char* optp, const char* argp) override { body; } \ + } + +V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbCall, void(void), m_cb(), en::NONE); +V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbOnOff, void(bool), m_cb(!hasPrefixNo(optp)), en::ONOFF); +template <> +V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbVal, void(int), m_cb(std::atoi(argp)), en::VALUE); +template <> +V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbVal, void(const char*), m_cb(argp), + en::VALUE); +V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbPartialMatch, void(const char*), m_cb(optp), en::NONE, + true); +V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbPartialMatchVal, void(const char*, const char*), + m_cb(optp, argp), en::VALUE, true); + +#undef V3OPTION_PARSER_DEF_ACT_CB_CLASS + +//###################################################################### +// Member functions of V3OptionParser + +V3OptionParser::ActionIfs* V3OptionParser::find(const char* optp) { + auto it = m_pimpl->m_options.find(optp); + if (it != m_pimpl->m_options.end()) return it->second.get(); + for (auto&& act : m_pimpl->m_options) { + if (act.second->isOnOffAllowed()) { // Find starts with "-no" + const char* const nop = std::strncmp(optp, "-no", 3) ? nullptr : (optp + 3); + if (nop && (act.first == nop || act.first == (string{"-"} + nop))) { + return act.second.get(); + } + } else if (act.second->isPartialMatchAllowed()) { + if (!std::strncmp(optp, act.first.c_str(), act.first.length())) { + return act.second.get(); + } + } + } + return nullptr; +} + +template +V3OptionParser::ActionIfs& V3OptionParser::add(const std::string& opt, ARG arg) { + UASSERT(!m_pimpl->m_isFinalized, "Cannot add after finalize() is called"); + std::unique_ptr act{new ACT{std::move(arg)}}; + UASSERT(opt.size() >= 2, opt << " is too short"); + UASSERT(opt[0] == '-' || opt[0] == '+', opt << " does not start with either '-' or '+'"); + UASSERT(!(opt[0] == '-' && opt[1] == '-'), "Option must have single '-', but " << opt); + const auto insertedResult = m_pimpl->m_options.emplace(opt, std::move(act)); + UASSERT(insertedResult.second, opt << " is already registered"); + return *insertedResult.first->second; +} + +bool V3OptionParser::hasPrefixNo(const char* strp) { + UASSERT(strp[0] == '-', strp << " does not start with '-'"); + if (strp[1] == '-') ++strp; + return std::strncmp(strp, "-no", 3) == 0; +} + +int V3OptionParser::parse(int idx, int argc, char* argv[]) { + UASSERT(m_pimpl->m_isFinalized, "finalize() must be called before parse()"); + const char* optp = argv[idx]; + if (optp[0] == '-' && optp[1] == '-') ++optp; + ActionIfs* actp = find(optp); + if (!actp) return 0; + if (!actp->isValueNeeded()) { + actp->exec(optp, nullptr); + return 1; + } else if (idx + 1 < argc) { + actp->exec(optp, argv[idx + 1]); + return 2; + } + return 0; +} + +string V3OptionParser::getSuggestion(const char* str) const { + return m_pimpl->m_spellCheck.bestCandidateMsg(str); +} + +void V3OptionParser::addSuggestionCandidate(const string& s) { + m_pimpl->m_spellCheck.pushCandidate(s); +} + +void V3OptionParser::finalize() { + UASSERT(!m_pimpl->m_isFinalized, "finalize() must not be called twice"); + for (auto&& opt : m_pimpl->m_options) { + if (opt.second->isUndocumented()) continue; + m_pimpl->m_spellCheck.pushCandidate(opt.first); + if (opt.second->isOnOffAllowed()) m_pimpl->m_spellCheck.pushCandidate("-no" + opt.first); + } + m_pimpl->m_isFinalized = true; +} + +V3OptionParser::V3OptionParser() + : m_pimpl{new Impl{}} {} + +V3OptionParser::~V3OptionParser() = default; + +//###################################################################### +// Member functions of V3OptionParser::AppendHelper + +#define V3OPTION_PARSER_DEF_OP(actKind, argType, actType) \ + V3OptionParser::ActionIfs& V3OptionParser::AppendHelper::operator()( \ + const char* optp, actKind, argType arg) const { \ + return m_parser.add(optp, arg); \ + } +V3OPTION_PARSER_DEF_OP(Set, bool*, ActionSet) +#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL +V3OPTION_PARSER_DEF_OP(Set, VOptionBool*, ActionSet) +#endif +V3OPTION_PARSER_DEF_OP(Set, int*, ActionSet) +V3OPTION_PARSER_DEF_OP(Set, string*, ActionSet) +V3OPTION_PARSER_DEF_OP(OnOff, bool*, ActionOnOff) +#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL +V3OPTION_PARSER_DEF_OP(OnOff, VOptionBool*, ActionOnOff) +#endif +V3OPTION_PARSER_DEF_OP(CbCall, Impl::ActionCbCall::CbType, ActionCbCall) +V3OPTION_PARSER_DEF_OP(CbOnOff, Impl::ActionCbOnOff::CbType, ActionCbOnOff) +V3OPTION_PARSER_DEF_OP(CbVal, Impl::ActionCbVal::CbType, ActionCbVal) +V3OPTION_PARSER_DEF_OP(CbVal, Impl::ActionCbVal::CbType, ActionCbVal) +#undef V3OPTION_PARSER_DEF_OP + +V3OptionParser::ActionIfs& +V3OptionParser::AppendHelper::operator()(const char* optp, CbPartialMatch, + Impl::ActionCbPartialMatch::CbType cb) const { + const size_t prefixLen = std::strlen(optp); + auto wrap = [prefixLen, cb](const char* optp) { cb(optp + prefixLen); }; + return m_parser.add(optp, std::move(wrap)); +} + +V3OptionParser::ActionIfs& +V3OptionParser::AppendHelper::operator()(const char* optp, CbPartialMatchVal, + Impl::ActionCbPartialMatchVal::CbType cb) const { + const size_t prefixLen = std::strlen(optp); + auto wrap + = [prefixLen, cb](const char* optp, const char* argp) { cb(optp + prefixLen, argp); }; + return m_parser.add(optp, std::move(wrap)); +} diff --git a/src/V3OptionParser.h b/src/V3OptionParser.h new file mode 100644 index 000000000..42ad88135 --- /dev/null +++ b/src/V3OptionParser.h @@ -0,0 +1,156 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Command line option parser +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 by Wilson Snyder. This program is free software; you +// can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifndef VERILATOR_V3OPTION_PARSER_H_ +#define VERILATOR_V3OPTION_PARSER_H_ + +#include "config_build.h" +#include "verilatedos.h" + +#include +#include +#include + +// Not to include V3Option.h here so that VlcMain or other executables can use this file easily. +#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL +class VOptionBool; +#endif + +// Typycal usage would look as below. +// See also V3Options::parseOptsList() in V3Optoins.cpp for more detailed usage. +// +// V3OptionParser parser; +// V3OptionParser::AppendHelper DECL_OPTION{parser}; +// V3OPTION_PARSER_DECL_TAGS; +// +// DECL_OPPTION("-help", CbCall, []() { showHelp(); }); +// DECL_OPPTION("-some_opt", Set, &m_intMember}); +// parser.finalize(); +// for (int i = 1; i < argc;) { +// if (int consumed = parser.parse(i, argc, argv)) { +// i += consumed; +// } else { // error +// cerr << parser.getSuggestion(argv[i]) << endl; +// } +// } +// + +//###################################################################### +// V3 OptionParser + +class V3OptionParser final { +public: + // TYPES + class ActionIfs; + // Functor to register options to V3OptionParser + class AppendHelper; + struct Impl; + +private: + // MEMBERS + std::unique_ptr m_pimpl; + + // METHODS + ActionIfs* find(const char* optp); + template ActionIfs& add(const string& opt, ARG arg); + static bool hasPrefixNo(const char* strp); // Returns true if strp starts with "-no" + +public: + // METHODS + // Returns how many args are consumed. 0 means not match + int parse(int idx, int argc, char* argv[]); + // Find the most similar option + string getSuggestion(const char* str) const; + void addSuggestionCandidate(const string& s); + // Call this function after all options are registered. + void finalize(); + + // CONSTRUCTORS + V3OptionParser(); + ~V3OptionParser(); +}; + +class V3OptionParser::ActionIfs VL_NOT_FINAL { +public: + virtual ~ActionIfs() = default; + virtual bool isValueNeeded() const = 0; // Need val of "-opt val" + virtual bool isOnOffAllowed() const = 0; // true if "-no-opt" is allowd + virtual bool isPartialMatchAllowed() const = 0; // true if "-Wno-" matches "-Wno-fatal" + virtual bool isUndocumented() const = 0; // Will not be suggested in typo + // Set a value or run callback + virtual void exec(const char* optp, const char* valp) = 0; + // Mark this option undocumented. (Exclude this option from suggestion list). + virtual void undocumented() = 0; +}; + +// A helper class to register options +class V3OptionParser::AppendHelper final { +public: + // TYPES + // Tag to specify which operator() to call + struct Set {}; // For ActionSet + struct OnOff {}; // For ActionOnOff + struct CbCall {}; // For ActionCbCall + struct CbOnOff {}; // For ActionOnOff + struct CbVal {}; // For ActionCbVal + struct CbPartialMatch {}; // For ActionCbPartialMatch + struct CbPartialMatchVal {}; // For ActionCbPartialMatchVal + +private: + // MEMBERS + V3OptionParser& m_parser; // The actual option registory + +public: + // METHODS + ActionIfs& operator()(const char* optp, Set, bool*) const; +#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL + ActionIfs& operator()(const char* optp, Set, VOptionBool*) const; +#endif + ActionIfs& operator()(const char* optp, Set, int*) const; + ActionIfs& operator()(const char* optp, Set, string*) const; + + ActionIfs& operator()(const char* optp, OnOff, bool*) const; +#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL + ActionIfs& operator()(const char* optp, OnOff, VOptionBool*) const; +#endif + + ActionIfs& operator()(const char* optp, CbCall, std::function) const; + ActionIfs& operator()(const char* optp, CbOnOff, std::function) const; + ActionIfs& operator()(const char* optp, CbVal, std::function) const; + ActionIfs& operator()(const char* optp, CbVal, std::function) const; + + ActionIfs& operator()(const char* optp, CbPartialMatch, + std::function) const; + ActionIfs& operator()(const char* optp, CbPartialMatchVal, + std::function) const; + + // CONSTRUCTORS + explicit AppendHelper(V3OptionParser& parser) + : m_parser{parser} {} +}; + +#define V3OPTION_PARSER_DECL_TAGS \ + const auto Set = V3OptionParser::AppendHelper::Set{}; \ + const auto OnOff = V3OptionParser::AppendHelper::OnOff{}; \ + const auto CbCall = V3OptionParser::AppendHelper::CbCall{}; \ + const auto CbOnOff = V3OptionParser::AppendHelper::CbOnOff{}; \ + const auto CbVal = V3OptionParser::AppendHelper::CbVal{}; \ + const auto CbPartialMatch = V3OptionParser::AppendHelper::CbPartialMatch{}; \ + const auto CbPartialMatchVal = V3OptionParser::AppendHelper::CbPartialMatchVal {} + +//###################################################################### + +#endif // guard diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 244d890d0..7533f01a8 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -19,9 +19,9 @@ #include "V3Global.h" #include "V3Ast.h" -#include "V3String.h" #include "V3Os.h" #include "V3Options.h" +#include "V3OptionParser.h" #include "V3Error.h" #include "V3File.h" #include "V3PreShell.h" @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -106,226 +105,6 @@ public: ~V3OptionsImp() = default; }; -//###################################################################### -// V3 OptionParser - -class V3OptionsParser final { -public: - // TYPES - class ActionIfs VL_NOT_FINAL { - public: - virtual ~ActionIfs() = default; - virtual bool isValueNeeded() const = 0; // Need val of "-opt val" - virtual bool isOnOffAllowed() const = 0; // true if "-no-opt" is allowd - virtual bool isPartialMatchAllowed() const = 0; // true if "-Wno-" matches "-Wno-fatal" - virtual bool isUndocumented() const = 0; // Will not be suggested in typo - // Set a value or run callback - virtual void exec(const char* optp, const char* valp) = 0; - virtual void undocumented() = 0; - }; - enum class en { // Setting for isOnOffAllowed() and isPartialMatchAllowed() - NONE, // "-opt" - ONOFF, // "-opt" and "-no-opt" - VALUE // "-opt val" - }; - // Base class of actual action classes - template - class ActionBase VL_NOT_FINAL : public ActionIfs { - bool m_undocumented = false; // This option is not documented - public: - virtual bool isValueNeeded() const override final { return MODE == en::VALUE; } - virtual bool isOnOffAllowed() const override final { return MODE == en::ONOFF; } - virtual bool isPartialMatchAllowed() const override final { return ALLOW_PARTIAL_MATCH; } - virtual bool isUndocumented() const override { return m_undocumented; } - virtual void undocumented() override { m_undocumented = true; } - }; - // Actual action classes - template class ActionSet; // "-opt" for bool-ish, "-opt val" for int and string - template class ActionOnOff; // "-opt" and "-no-opt" for bool-ish - class ActionCbCall; // Callback without argument for "-opt" - class ActionCbOnOff; // Callback for "-opt" and "-no-opt" - template class ActionCbVal; // Callback for "-opt val" - class ActionCbPartialMatch; // Callback "-O3" for "-O" - class ActionCbPartialMatchVal; // Callback "-debugi-V3Options 3" for "-debugi-" - - // Functor to register options to V3OptionsParser - struct AppendHelper; - -private: - // MEMBERS - std::map> m_options; // All actions for option - bool m_isFinalized{false}; // Becomes after finalize() is called - VSpellCheck m_spellCheck; // Suggests closest option when not found - - // METHODS - ActionIfs* find(const char* optp) { - auto it = m_options.find(optp); - if (it != m_options.end()) return it->second.get(); - for (auto&& act : m_options) { - if (act.second->isOnOffAllowed()) { // Find starts with "-no" - const char* const nop = std::strncmp(optp, "-no", 3) ? nullptr : (optp + 3); - if (nop && (act.first == nop || act.first == (string{"-"} + nop))) { - return act.second.get(); - } - } else if (act.second->isPartialMatchAllowed()) { - if (!std::strncmp(optp, act.first.c_str(), act.first.length())) { - return act.second.get(); - } - } - } - return nullptr; - } - template ActionIfs& add(const std::string& opt, ARG arg) { - UASSERT(!m_isFinalized, "Cannot add after finalize() is called"); - std::unique_ptr act{new ACT{std::move(arg)}}; - UASSERT(opt.size() >= 2, opt << " is too short"); - UASSERT(opt[0] == '-' || opt[0] == '+', opt << " does not start with either '-' or '+'"); - UASSERT(!(opt[0] == '-' && opt[1] == '-'), "Option must have single '-', but " << opt); - const auto insertedResult = m_options.emplace(opt, std::move(act)); - UASSERT(insertedResult.second, opt << " is already registered"); - return *insertedResult.first->second; - } - static bool hasPrefixNo(const char* strp) { // Returns true if strp starts with "-no" - UASSERT(strp[0] == '-', strp << " does not start with '-'"); - if (strp[1] == '-') ++strp; - return std::strncmp(strp, "-no", 3) == 0; - } - -public: - // METHODS - // Returns how many args are consumed. 0 means not match - int parse(int idx, int argc, char* argv[]) { - UASSERT(m_isFinalized, "finalize() must be called before parse()"); - const char* optp = argv[idx]; - if (optp[0] == '-' && optp[1] == '-') ++optp; - ActionIfs* actp = find(optp); - if (!actp) return 0; - if (!actp->isValueNeeded()) { - actp->exec(optp, nullptr); - return 1; - } else if (idx + 1 < argc) { - actp->exec(optp, argv[idx + 1]); - return 2; - } - return 0; - } - // Find the most similar option - string getSuggestion(const char* str) const { return m_spellCheck.bestCandidateMsg(str); } - void addSuggestionCandidate(const string& s) { m_spellCheck.pushCandidate(s); } - void finalize() { - UASSERT(!m_isFinalized, "finalize() must not be called twice"); - for (auto&& opt : m_options) { - if (opt.second->isUndocumented()) continue; - m_spellCheck.pushCandidate(opt.first); - if (opt.second->isOnOffAllowed()) m_spellCheck.pushCandidate("-no" + opt.first); - } - m_isFinalized = true; - } -}; - -#define V3OPTIONS_DEF_ACT_CLASS(className, type, body, enType) \ - template <> class V3OptionsParser::className final : public ActionBase { \ - type* m_valp; /* Pointer to a option variable*/ \ -\ - public: \ - explicit className(type* valp) \ - : m_valp(valp) {} \ - virtual void exec(const char* optp, const char* argp) override { body; } \ - } - -V3OPTIONS_DEF_ACT_CLASS(ActionSet, bool, *m_valp = true, en::NONE); -V3OPTIONS_DEF_ACT_CLASS(ActionSet, VOptionBool, m_valp->setTrueOrFalse(true), en::NONE); -V3OPTIONS_DEF_ACT_CLASS(ActionSet, int, *m_valp = std::atoi(argp), en::VALUE); -V3OPTIONS_DEF_ACT_CLASS(ActionSet, string, *m_valp = argp, en::VALUE); - -V3OPTIONS_DEF_ACT_CLASS(ActionOnOff, bool, *m_valp = !hasPrefixNo(optp), en::ONOFF); -V3OPTIONS_DEF_ACT_CLASS(ActionOnOff, VOptionBool, m_valp->setTrueOrFalse(!hasPrefixNo(optp)), - en::ONOFF); - -#undef V3OPTIONS_DEF_ACT_CLASS - -#define V3OPTIONS_DEF_ACT_CB_CLASS(className, funcType, body, ...) \ - class V3OptionsParser::className final : public ActionBase<__VA_ARGS__> { \ - std::function m_cb; /* Callback function */ \ -\ - public: \ - using CbType = std::function; \ - explicit className(CbType cb) \ - : m_cb(std::move(cb)) {} \ - virtual void exec(const char* optp, const char* argp) override { body; } \ - } - -V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbCall, void(void), m_cb(), en::NONE); -V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbOnOff, void(bool), m_cb(!hasPrefixNo(optp)), en::ONOFF); -template <> -V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbVal, void(int), m_cb(std::atoi(argp)), en::VALUE); -template <> -V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbVal, void(const char*), m_cb(argp), en::VALUE); -V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbPartialMatch, void(const char*), m_cb(optp), en::NONE, true); -V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbPartialMatchVal, void(const char*, const char*), - m_cb(optp, argp), en::VALUE, true); - -#undef V3OPTIONS_DEF_ACT_CB_CLASS - -// A helper class to register options -struct V3OptionsParser::AppendHelper final { - V3OptionsParser& m_parser; // The actual option registory - - // TYPES - // Tag to specify which operator() to call - struct Set {}; // For ActionSet - struct OnOff {}; // For ActionOnOff - struct CbCall {}; // For ActionCbCall - struct CbOnOff {}; // For ActionOnOff - struct CbVal {}; // For ActionCbVal - struct CbPartialMatch {}; // For ActionCbPartialMatch - struct CbPartialMatchVal {}; // For ActionCbPartialMatchVal - - // METHODS - -#define V3OPTIONS_DEF_OP(actKind, argType, actType) \ - ActionIfs& operator()(const char* optp, actKind, argType arg) const { \ - return m_parser.add(optp, arg); \ - } - V3OPTIONS_DEF_OP(Set, bool*, ActionSet) - V3OPTIONS_DEF_OP(Set, VOptionBool*, ActionSet) - V3OPTIONS_DEF_OP(Set, int*, ActionSet) - V3OPTIONS_DEF_OP(Set, string*, ActionSet) - V3OPTIONS_DEF_OP(OnOff, bool*, ActionOnOff) - V3OPTIONS_DEF_OP(OnOff, VOptionBool*, ActionOnOff) - V3OPTIONS_DEF_OP(CbCall, ActionCbCall::CbType, ActionCbCall) - V3OPTIONS_DEF_OP(CbOnOff, ActionCbOnOff::CbType, ActionCbOnOff) - V3OPTIONS_DEF_OP(CbVal, ActionCbVal::CbType, ActionCbVal) - V3OPTIONS_DEF_OP(CbVal, ActionCbVal::CbType, ActionCbVal) -#undef V3OPTIONS_DEF_OP - - // Syntax sugar to register a member function of V3Options directry - ActionIfs& operator()(const char* optp, CbVal, - std::pair arg) const { - auto cb = [arg](int v) { (arg.first->*arg.second)(v); }; - return m_parser.add>(optp, cb); - } - ActionIfs& operator()(const char* optp, CbVal, - std::pair arg) const { - auto cb = [arg](const string& v) { (arg.first->*arg.second)(v); }; - return m_parser.add>(optp, cb); - } - // Callback of partial match expects prefix to be removed, so wrap the given callback - ActionIfs& operator()(const char* optp, CbPartialMatch, - ActionCbPartialMatch::CbType cb) const { - const size_t prefixLen = std::strlen(optp); - auto wrap = [prefixLen, cb](const char* optp) { cb(optp + prefixLen); }; - return m_parser.add(optp, std::move(wrap)); - } - ActionIfs& operator()(const char* optp, CbPartialMatchVal, - ActionCbPartialMatchVal::CbType cb) const { - const size_t prefixLen = std::strlen(optp); - auto wrap - = [prefixLen, cb](const char* optp, const char* argp) { cb(optp + prefixLen, argp); }; - return m_parser.add(optp, std::move(wrap)); - } -}; - //###################################################################### // V3LangCode class functions @@ -1112,20 +891,18 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char addArg(argv[i]); // -f's really should be inserted in the middle, but this is for debug } - V3OptionsParser parser; - const auto Set = V3OptionsParser::AppendHelper::Set{}; - const auto OnOff = V3OptionsParser::AppendHelper::OnOff{}; - const auto CbCall = V3OptionsParser::AppendHelper::CbCall{}; - const auto CbOnOff = V3OptionsParser::AppendHelper::CbOnOff{}; - const auto CbVal = V3OptionsParser::AppendHelper::CbVal{}; - const auto CbPartialMatch = V3OptionsParser::AppendHelper::CbPartialMatch{}; - const auto CbPartialMatchVal = V3OptionsParser::AppendHelper::CbPartialMatchVal{}; - V3OptionsParser::AppendHelper DECL_OPTION{parser}; + V3OptionParser parser; + V3OptionParser::AppendHelper DECL_OPTION{parser}; + V3OPTION_PARSER_DECL_TAGS; + + auto callStrSetter = [this](void (V3Options::*cbStr)(const string&)) { + return [this, cbStr](const string& v) { (this->*cbStr)(v); }; + }; // Usage // DECL_OPTION("-option", action, pointer_or_lambda); // action: one of Set, OnOff, CbCall, CbOnOff, CbVal, CbPartialMatch, and CbPartialMatchVal // Set : Set value to a variable, pointer_or_lambda must be a pointer to the - // variable. + // variable. // true is set to bool-ish variable when '-opt' is passed to verilator. // val is set to int and string variable when '-opt val' is passed. // OnOff : Set value to a bool-ish variable, pointer_or_lambda must be a pointer @@ -1138,7 +915,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char // CbVal : Call lambda or function that takes int or const char*. // "-opt val" is passed to verilator, val is passed to the lambda. // If a function to be called is a member of V3Options that only takes - // bool/const string&, {this, &V3Options::memberFunc} can be passed + // const string&, callStrSetter(&V3Options::memberFunc) can be passed // instead of lambda as a syntax sugar. // CbPartialMatch : Call lambda or function that takes remaining string. // e.g. DECL_OPTION("-opt-", CbPartialMatch, [](const char*optp) { cout << @@ -1201,14 +978,14 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char DECL_OPTION("-bin", Set, &m_bin); DECL_OPTION("-build", Set, &m_build); - DECL_OPTION("-CFLAGS", CbVal, {this, &V3Options::addCFlags}); + DECL_OPTION("-CFLAGS", CbVal, callStrSetter(&V3Options::addCFlags)); DECL_OPTION("-cc", CbCall, [this]() { m_outFormatOk = true; m_systemC = false; }); DECL_OPTION("-cdc", OnOff, &m_cdc); - DECL_OPTION("-clk", CbVal, {this, &V3Options::addClocker}); - DECL_OPTION("-no-clk", CbVal, {this, &V3Options::addNoClocker}); + DECL_OPTION("-clk", CbVal, callStrSetter(&V3Options::addClocker)); + DECL_OPTION("-no-clk", CbVal, callStrSetter(&V3Options::addNoClocker)); DECL_OPTION("-comp-limit-blocks", Set, &m_compLimitBlocks).undocumented(); DECL_OPTION("-comp-limit-members", Set, &m_compLimitMembers) @@ -1244,7 +1021,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char DECL_OPTION("-D", CbPartialMatch, [this](const char* valp) { addDefine(valp, false); }); DECL_OPTION("-debug", CbCall, [this]() { setDebugMode(3); }); - DECL_OPTION("-debugi", CbVal, {this, &V3Options::setDebugMode}); + DECL_OPTION("-debugi", CbVal, [this](int v) { setDebugMode(v); }); DECL_OPTION("-debugi-", CbPartialMatchVal, [this](const char* optp, const char* valp) { setDebugSrcLevel(optp, std::atoi(valp)); }); @@ -1320,7 +1097,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char }); DECL_OPTION("-inline-mult", Set, &m_inlineMult); - DECL_OPTION("-LDFLAGS", CbVal, {this, &V3Options::addLdLibs}); + DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs)); auto setLang = [this, fl](const char* valp) { V3LangCode optval = V3LangCode(valp); if (optval.legal()) { @@ -1340,7 +1117,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented(); // Historical - DECL_OPTION("-MAKEFLAGS", CbVal, {this, &V3Options::addMakeFlags}); + DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags)); DECL_OPTION("-MMD", OnOff, &m_makeDepend); DECL_OPTION("-MP", OnOff, &m_makePhony); DECL_OPTION("-Mdir", CbVal, [this](const char* valp) {