diff --git a/clasp/cli/clasp_cli_options.inl b/clasp/cli/clasp_cli_options.inl index 9659f47..24d0dc7 100644 --- a/clasp/cli/clasp_cli_options.inl +++ b/clasp/cli/clasp_cli_options.inl @@ -1,5 +1,5 @@ // -// Copyright (c) 2013-2017 Benjamin Kaufmann +// Copyright (c) 2013-present Benjamin Kaufmann // // This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ // @@ -89,7 +89,10 @@ #endif //! Options for configuring a SharedContext object stored in a Clasp::ContextParams object. -#if defined(CLASP_CONTEXT_OPTIONS) +#if defined(CLASP_CONTEXT_OPTIONS) || defined(CLASP_ALL_GROUPS) +#if !defined(CLASP_CONTEXT_OPTIONS) +#define CLASP_CONTEXT_OPTIONS (*base) +#endif #define SELF CLASP_CONTEXT_OPTIONS GROUP_BEGIN(SELF) OPTION(share, "!,@1", ARG_EXT(defaultsTo("auto")->state(Value::value_defaulted), DEFINE_ENUM_MAPPING(ContextParams::ShareMode, \ @@ -118,7 +121,10 @@ GROUP_END(SELF) #endif //! Global options only valid in facade. -#if defined(CLASP_GLOBAL_OPTIONS) +#if defined(CLASP_GLOBAL_OPTIONS) || defined(CLASP_ALL_GROUPS) +#if !defined(CLASP_GLOBAL_OPTIONS) +#define CLASP_GLOBAL_OPTIONS (*this) +#endif #define SELF CLASP_GLOBAL_OPTIONS GROUP_BEGIN(SELF) OPTION(stats, ",s", ARG(implicit("1")->arg("[,]")), "Enable {1=basic|2=full} statistics ( for tester)",\ @@ -139,7 +145,10 @@ GROUP_END(SELF) #endif //! Solver options (see SolverParams). -#if defined(CLASP_SOLVER_OPTIONS) +#if defined(CLASP_SOLVER_OPTIONS) || defined(CLASP_ALL_GROUPS) +#if !defined(CLASP_SOLVER_OPTIONS) +#define CLASP_SOLVER_OPTIONS (*solver) +#endif #define SELF CLASP_SOLVER_OPTIONS GROUP_BEGIN(SELF) OPTION(opt_strategy , "" , ARG_EXT(arg(""),\ @@ -318,7 +327,10 @@ GROUP_END(SELF) #endif //! Search-related options (see SolveParams). -#if defined(CLASP_SEARCH_OPTIONS) +#if defined(CLASP_SEARCH_OPTIONS) || defined(CLASP_ALL_GROUPS) +#if !defined(CLASP_SEARCH_OPTIONS) +#define CLASP_SEARCH_OPTIONS (*search) +#endif #define SELF CLASP_SEARCH_OPTIONS GROUP_BEGIN(SELF) OPTION(partial_check, "", ARG(implicit("50")), "Configure partial stability tests\n" \ @@ -338,6 +350,9 @@ OPTION(rand_prob, "", ARG(arg("[,]")), "Do random searches with [=1 GET_IF(SELF.randRuns, SELF.randRuns,SELF.randConf)) #undef SELF //! Options for configuring the restart strategy of a solver. +#if !defined(CLASP_SEARCH_RESTART_OPTIONS) +#define CLASP_SEARCH_RESTART_OPTIONS (search->restart) +#endif #define SELF CLASP_SEARCH_RESTART_OPTIONS #if defined(NOTIFY_SUBGROUPS) GROUP_BEGIN(SELF) @@ -383,6 +398,9 @@ GROUP_END(SELF) #undef SELF #undef CLASP_SEARCH_RESTART_OPTIONS //! Options for configuring the deletion strategy of a solver. +#if !defined(CLASP_SEARCH_REDUCE_OPTIONS) +#define CLASP_SEARCH_REDUCE_OPTIONS (search->reduce) +#endif #define SELF CLASP_SEARCH_REDUCE_OPTIONS #if defined(NOTIFY_SUBGROUPS) GROUP_BEGIN(SELF) @@ -439,7 +457,10 @@ GROUP_END(CLASP_SEARCH_OPTIONS) #endif //! ASP-front-end options stored in an Clasp::Asp::LogicProgram::AspOptions object. -#if defined(CLASP_ASP_OPTIONS) +#if defined(CLASP_ASP_OPTIONS) || defined(CLASP_ALL_GROUPS) +#if !defined(CLASP_ASP_OPTIONS) +#define CLASP_ASP_OPTIONS (this->asp) +#endif #define SELF CLASP_ASP_OPTIONS GROUP_BEGIN(SELF) OPTION(trans_ext , "!", ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(Asp::LogicProgram::ExtendedRuleMode,\ @@ -470,7 +491,10 @@ GROUP_END(SELF) #endif //! Options for the solving algorithm (see Clasp::SolveOptions) -#if defined(CLASP_SOLVE_OPTIONS) +#if defined(CLASP_SOLVE_OPTIONS) || defined(CLASP_ALL_GROUPS) +#if !defined(CLASP_SOLVE_OPTIONS) +#define CLASP_SOLVE_OPTIONS (this->solve) +#endif #define SELF CLASP_SOLVE_OPTIONS GROUP_BEGIN(SELF) OPTION(solve_limit , ",@1", ARG(arg("[,]")), "Stop search after conflicts or restarts\n", FUN(arg) {\ @@ -574,3 +598,4 @@ GROUP_END(SELF) #undef ARG #undef ARG_EXT #undef NO_ARG +#undef CLASP_ALL_GROUPS diff --git a/clasp/cli/clasp_options.h b/clasp/cli/clasp_options.h index 32d7f15..47d0dcd 100644 --- a/clasp/cli/clasp_options.h +++ b/clasp/cli/clasp_options.h @@ -258,45 +258,32 @@ class ClaspCliConfig : public ClaspConfig { //@} private: struct ParseContext; - struct OptIndex; class ProgOption; typedef Potassco::ProgramOptions::OptionContext OptionContext; typedef Potassco::ProgramOptions::OptionGroup Options; typedef SingleOwnerPtr OptionsPtr; typedef Potassco::ProgramOptions::ParsedOptions ParsedOpts; - struct ScopedSet { - ScopedSet(ClaspCliConfig& s, uint8 mode, uint32 sId = 0); - ~ScopedSet(); - ClaspCliConfig* operator->()const { return self; } - ClaspCliConfig* self; - }; // Operations on active config and solver - int setOption(int option, const char* value, const ParsedOpts* exclude = 0); + int setOption(int option, uint8 mode, uint32 sId, const char* value); // App interface impl bool setAppConfig(const std::string& c, ProblemType t); - int setAppOpt(int o, const char* value); - bool setAppDefaults(UserConfig* active, ConfigKey config, const ParsedOpts& exclude, ProblemType t); - bool finalizeAppConfig(UserConfig* active, const ParsedOpts& exclude, ProblemType t, bool defs); - const ParsedOpts& finalizeParsed(UserConfig& active, const ParsedOpts& parsed, ParsedOpts& exclude) const; + int setAppOpt(int o, uint8 mode, const char* value); + bool setAppDefaults(ConfigKey config, uint8 mode, const ParsedOpts& exclude, ProblemType t); + bool finalizeAppConfig(uint8 mode, const ParsedOpts& exclude, ProblemType t, bool defs); + const ParsedOpts& finalizeParsed(uint8 mode, const ParsedOpts& parsed, ParsedOpts& exclude) const; void createOptions(); ProgOption* createOption(int o); const std::string&getOptionName(int key, std::string& mem) const; bool assignDefaults(const ParsedOpts&); // Configurations - static bool appendConfig(std::string& to, const std::string& line); - static bool loadConfig(std::string& to, const char* fileName); - ConfigIter getConfig(uint8 key, std::string& tempMem); - bool setConfig(const char* name, const char* args, bool allowAppOpt, const ParsedOpts& exclude, ParsedOpts* out); - bool setConfig(const ConfigIter& c, bool allowAppOpt, const ParsedOpts& exclude, ParsedOpts* out); + ConfigIter getConfig(uint8 key, std::string& tempMem) const; + bool setConfig(const char* name, const char* args, uint8 mode, uint32 sId, const ParsedOpts& exclude, ParsedOpts* out); + bool setConfig(const ConfigIter& c, uint8 mode, uint32 sId, const ParsedOpts& exclude, ParsedOpts* out); // helpers - bool isGenerator() const; - UserConfig* active(); - static OptIndex index_g; - OptionsPtr opts_; - std::string config_[2]; - uint8 mode_; - uint8 solver_; - bool initTester_; + OptionsPtr opts_; + ParseContext* parseCtx_; + std::string config_[2]; + bool validate_; }; //! Validates the given solver configuration and returns an error string if invalid. const char* validate(const SolverParams& solver, const SolveParams& search); diff --git a/src/clasp_options.cpp b/src/clasp_options.cpp index a9d2870..d843345 100644 --- a/src/clasp_options.cpp +++ b/src/clasp_options.cpp @@ -48,6 +48,7 @@ #define SET_OR_FILL(x, v) ( SET((x),(v)) || ((x) = 0, (x) = ~(x),true) ) #define SET_OR_ZERO(x,v) ( SET((x),(v)) || SET((x),uint32(0)) ) #define SET_R(x, v, lo, hi) ( ((lo)<=(v)) && ((v)<=(hi)) && SET((x), (v)) ) +#define ITE(c, a, b) (!!(c) ? (a) : (b)) ///////////////////////////////////////////////////////////////////////////////////////// // Primitive types/functions for string <-> T conversions ///////////////////////////////////////////////////////////////////////////////////////// @@ -224,12 +225,7 @@ static std::string& xconvert(std::string& out, X x) { \ return out.append(Potassco::findKey(enumMap(&x), static_cast(x))); \ } #define OPTION(k, e, a, d, ...) a -#define CLASP_CONTEXT_OPTIONS -#define CLASP_GLOBAL_OPTIONS -#define CLASP_SOLVE_OPTIONS -#define CLASP_ASP_OPTIONS -#define CLASP_SOLVER_OPTIONS -#define CLASP_SEARCH_OPTIONS +#define CLASP_ALL_GROUPS #define ARG_EXT(a, X) X #define ARG(a) #define NO_ARG @@ -419,10 +415,9 @@ namespace Cli { ///////////////////////////////////////////////////////////////////////////////////////// // Option -> Key mapping ///////////////////////////////////////////////////////////////////////////////////////// -/// \cond -// Valid option keys. +namespace { enum OptionKey { - detail__before_options = -1, + detail_before_options = -1, meta_config = 0, #define CLASP_CONTEXT_OPTIONS GRP(option_category_nodes_end, option_category_context_begin), #define CLASP_GLOBAL_OPTIONS GRP(option_category_context_end, option_category_global_begin), @@ -432,17 +427,13 @@ enum OptionKey { #define CLASP_SOLVE_OPTIONS GRP(option_category_asp_end, option_category_solve_begin), #define OPTION(k,e,...) opt_##k, #define GROUP_BEGIN(X) X -#define GRP(X, Y) X, Y = X, detail__before_##Y = X - 1 +#define GRP(X, Y) X, Y = X, detail_before_##Y = X - 1 #include #undef GRP option_category_solve_end, - detail__num_options = option_category_solve_end, - meta_tester = detail__num_options + detail_num_options = option_category_solve_end, + meta_tester = detail_num_options }; -static inline bool isOption(int k) { return k >= option_category_nodes_end && k < detail__num_options; } -static inline bool isGlobalOption(int k) { return k >= option_category_global_begin && k < option_category_global_end; } -static inline bool isTesterOption(int k) { return k >= option_category_nodes_end && k < option_category_search_end && !isGlobalOption(k); } -static inline bool isSolverOption(int k) { return k >= option_category_solver_begin && k < option_category_search_end; } #if CLASP_HAS_THREADS #define MANY_DESC " many : Use default portfolio to configure solver(s)\n" #define MANY_ARG "|many" @@ -461,145 +452,92 @@ desc " : {auto|frumpy|jumpy|tweety|handy|crafty|trendy" MANY_ARG "|: Use configuration file to configure solver(s)" - struct NodeKey { const char* name; const char* desc; - int16 skBegin; - int16 skEnd; - uint32 numSubkeys() const { return static_cast( skEnd - skBegin ); } + int16 skBeg; + uint16 skSize; }; enum { key_leaf = 0, key_solver = -1, key_asp = -2, key_solve = -3, key_tester = -4, key_root = -5 }; -// nodes_g[-k]: entry for key k -static const NodeKey nodes_g[] = { -/* 0: config */ {"configuration", KEY_INIT_DESC("Initializes this configuration\n"), 0,0}, -/* 1: */ {"solver", "Solver Options", option_category_solver_begin, option_category_search_end}, -/* 2: */ {"asp" , "Asp Options" , option_category_asp_begin, option_category_asp_end}, -/* 3: */ {"solve" , "Solve Options" , option_category_solve_begin, option_category_solve_end}, -/* 4: */ {"tester", "Tester Options", key_solver, option_category_context_end}, -/* 5: */ {"" , "Options" , key_tester, option_category_global_end} +struct Name2Id { + const char *name; + int key; + bool operator<(const Name2Id &rhs) const { return *this < rhs.name; } + bool operator<(const char *rhs) const { return std::strcmp(name, rhs) < 0; } +}; +Name2Id index_g[detail_num_options + 1] = { + {"configuration", meta_config}, +#define OPTION(k, e, ...) { #k, opt_##k }, +#define CLASP_ALL_GROUPS +#include + {"tester", meta_tester} }; -static uint32 makeKeyHandle(int16 kId, uint32 mode, uint32 sId) { +bool init_index_g = (std::sort(index_g, index_g + detail_num_options + 1), true); +} +/// \cond +// Valid option keys. +static inline bool isOption(int k) { return k >= option_category_nodes_end && k < detail_num_options; } +static inline bool isGlobalOption(int k) { return k >= option_category_global_begin && k < option_category_global_end; } +static inline bool isTesterOption(int k) { return k >= option_category_nodes_end && k < option_category_search_end && !isGlobalOption(k); } +static inline bool isSolverOption(int k) { return k >= option_category_solver_begin && k < option_category_search_end; } +static inline int16 decodeKey(uint32 key) { return static_cast(static_cast(key)); } +static inline uint8 decodeMode(uint32 key) { return static_cast( (key >> 24) ); } +static inline uint8 decodeSolver(uint32 key){ return static_cast( (key >> 16) ); } +static inline bool isValidId(int16 id) { return id >= key_root && id < detail_num_options; } +static inline bool isLeafId(int16 id) { return id >= key_leaf && id < detail_num_options; } +static inline uint32 makeKeyHandle(int16 kId, uint32 mode, uint32 sId) { assert(sId <= 255 && mode <= 255); return (mode << 24) | (sId << 16) | static_cast(kId); } -static int16 decodeKey(uint32 key) { return static_cast(static_cast(key)); } -static uint8 decodeMode(uint32 key) { return static_cast( (key >> 24) ); } -static uint8 decodeSolver(uint32 key){ return static_cast( (key >> 16) ); } -static bool isValidId(int16 id) { return id >= key_root && id < detail__num_options; } -static bool isLeafId(int16 id) { return id >= key_leaf && id < detail__num_options; } static const uint8 mode_solver = 1u; static const uint8 mode_tester = 2u; static const uint8 mode_relaxed= 4u; -const ClaspCliConfig::KeyType ClaspCliConfig::KEY_INVALID = static_cast(-1); -const ClaspCliConfig::KeyType ClaspCliConfig::KEY_ROOT = makeKeyHandle(key_root, 0, 0); -const ClaspCliConfig::KeyType ClaspCliConfig::KEY_SOLVER = makeKeyHandle(key_solver, 0, 0); -const ClaspCliConfig::KeyType ClaspCliConfig::KEY_TESTER = makeKeyHandle(key_tester, mode_tester, 0); - -struct Name2Id { - const char* name; int key; - bool operator<(const Name2Id& rhs) const { return std::strcmp(name, rhs.name) < 0; } -}; -static Name2Id options_g[detail__num_options+1] = { - {"configuration", meta_config}, -#define OPTION(k, e, ...) { #k, opt_##k }, -#define CLASP_CONTEXT_OPTIONS -#define CLASP_GLOBAL_OPTIONS -#define CLASP_SOLVER_OPTIONS -#define CLASP_SEARCH_OPTIONS -#define CLASP_ASP_OPTIONS -#define CLASP_SOLVE_OPTIONS -#include - {"tester" , meta_tester} -}; -struct ClaspCliConfig::OptIndex { - OptIndex(Name2Id* first, Name2Id* last) { - std::sort(begin = first, end = last); - } - int16 find(const char* needle, bool prefix) const { - Name2Id ret = { needle, -1 }; - Name2Id* it = std::lower_bound(begin, end, ret); - if (it != end) { - std::size_t len = std::strlen(needle); - if (std::strncmp(it->name, needle, len) == 0 && (!it->name[len] || prefix)) { - Name2Id* next = it + 1; - ret.key = !it->name[len] || next == end || std::strncmp(next->name, needle, len) != 0 ? it->key : -2; - } - } - return static_cast(ret.key); - } - Name2Id* begin; - Name2Id* end; -}; -ClaspCliConfig::OptIndex ClaspCliConfig::index_g(options_g, options_g + detail__num_options+1); -/// \endcond -static int execOption(int o, BasicSatConfig* config, uint8 cliMode, uint32 sId, const char* _val_, std::string* _val_out_, - const char** _desc_out_, const char** _name_out_) { - assert(isOption(o)); - SolverParams* solver = 0; - SolveParams* search = 0; - ContextParams* ctxOpts = config; - bool testerOnly = (cliMode & mode_tester) != 0; - bool solverOnly = (cliMode & mode_solver) != 0; - if (_val_ || _val_out_) { - if (!config || (testerOnly && !isTesterOption(o)) || (solverOnly && !isSolverOption(o))) { - return cliMode & mode_relaxed ? int(_val_ != 0) : -1; - } - if (isSolverOption(o)) { - solver = _val_ ? &config->addSolver(sId) : const_cast(&config->solver(sId)); - search = _val_ ? &config->addSearch(sId) : const_cast(&config->search(sId)); +static const uint8 mode_meta = 8u; +static inline bool isTester(uint8 mode) { return (mode & mode_tester) != 0; } +static inline bool isSolver(uint8 mode) { return (mode & mode_solver) != 0; } +static inline BasicSatConfig* active(ClaspConfig* config, uint8 mode) { + return !isTester(mode) ? config : config->testerConfig(); +} +static inline const BasicSatConfig* active(const ClaspConfig* config, uint8 mode) { + return active(const_cast(config), mode); +} +static int16 findOption(const char* needle, bool prefix) { + const Name2Id* end = index_g + detail_num_options + 1; + const Name2Id* it = std::lower_bound(const_cast(index_g), end, needle); + int ret = -1; + if (it != end) { + std::size_t len = std::strlen(needle); + if (std::strncmp(it->name, needle, len) == 0 && (!it->name[len] || prefix)) { + const Name2Id* next = it + 1; + ret = !it->name[len] || next == end || std::strncmp(next->name, needle, len) != 0 ? it->key : -2; } } - // action & helper macros used in get/set - using Potassco::xconvert; using Potassco::off; using Potassco::opt; - using Potassco::stringTo; using Potassco::toString; using Potassco::Set; - #define ITE(c, a, b) (!!(c) ? (a) : (b)) - #define FUN(x) for (Potassco::ArgString x = _val_;;) - #define STORE(obj) { return stringTo((_val_), obj); } - #define STORE_LEQ(x, y) { unsigned __n; return stringTo(_val_, __n) && SET_LEQ(x, __n, y); } - #define STORE_FLAG(x) { bool __b; return stringTo(_val_, __b) && SET(x, static_cast(__b)); } - #define STORE_OR_FILL(x) { unsigned __n; return stringTo(_val_, __n) && SET_OR_FILL(x, __n); } - #define STORE_U(E, x) { E __e; return stringTo((_val_), __e) && SET(x, static_cast(__e));} - #define GET_FUN(x) Potassco::StringRef x(*_val_out_); if (!x.out);else - #define GET(...) *_val_out_ = toString( __VA_ARGS__ ) - #define GET_IF(c, ...) *_val_out_ = ITE((c), toString(__VA_ARGS__), toString(off)) - switch(static_cast(o)) { - default: return -1; - #define OPTION(k, e, a, d, x, v) \ - case opt_##k:\ - if (_name_out_){ *_name_out_ = #k ; }\ - if (_val_) try { x ; }catch(...) {return 0;}\ - if (_val_out_) { v ; }\ - if (_desc_out_){ *_desc_out_ = d; }\ - return 1; - #define CLASP_CONTEXT_OPTIONS (*ctxOpts) - #define CLASP_GLOBAL_OPTIONS (*static_cast(config)) - #define CLASP_ASP_OPTIONS (static_cast(config)->asp) - #define CLASP_SOLVE_OPTIONS (static_cast(config)->solve) - #define CLASP_SOLVER_OPTIONS (*solver) - #define CLASP_SEARCH_OPTIONS (*search) - #define CLASP_SEARCH_RESTART_OPTIONS search->restart - #define CLASP_SEARCH_REDUCE_OPTIONS search->reduce - #include - } - #undef ITE - #undef FUN - #undef STORE - #undef STORE_LEQ - #undef STORE_FLAG - #undef STORE_OR_FILL - #undef STORE_U - #undef GET_FUN - #undef GET - #undef GET_IF + return static_cast(ret); +} +static NodeKey makeNode(const char* name, const char* desc, int16 skBeg = 0, int16 skEnd = 0) { + NodeKey n = {name, desc, skBeg, static_cast(skEnd - skBeg) }; + return n; } static NodeKey getNode(int16 id) { assert(isValidId(id)); - if (id <= key_leaf) { return nodes_g[-id]; } - NodeKey n = {0, 0, id, id}; - execOption(id, 0, 0, 0, 0, 0, &n.desc, &n.name); - return n; + switch(id) { + case key_root : return makeNode("", "Options", key_tester, option_category_global_end); + case key_tester: return makeNode("tester", "Tester Options", key_solver, option_category_context_end); + case key_solve : return makeNode("solve", "Solve Options", option_category_solve_begin, option_category_solve_end); + case key_asp : return makeNode("asp", "Asp Options", option_category_asp_begin, option_category_asp_end); + case key_solver: return makeNode("solver", "Solver Options", option_category_solver_begin, option_category_search_end); + case key_leaf : return makeNode("configuration", KEY_INIT_DESC("Initializes this configuration\n")); + #define OPTION(k, e, a, d, x, v) case opt_##k: return makeNode(#k, d); + #define CLASP_ALL_GROUPS + #include + default : return makeNode("", ""); + } } +const ClaspCliConfig::KeyType ClaspCliConfig::KEY_INVALID = static_cast(-1); +const ClaspCliConfig::KeyType ClaspCliConfig::KEY_ROOT = makeKeyHandle(key_root, 0, 0); +const ClaspCliConfig::KeyType ClaspCliConfig::KEY_SOLVER = makeKeyHandle(key_solver, 0, 0); +const ClaspCliConfig::KeyType ClaspCliConfig::KEY_TESTER = makeKeyHandle(key_tester, mode_tester, 0); +/// \endcond ///////////////////////////////////////////////////////////////////////////////////////// // Interface to ProgramOptions ///////////////////////////////////////////////////////////////////////////////////////// @@ -622,35 +560,42 @@ static void cliNameToKey(std::string& out, const char* n) { out.append(n); } // Type for storing one command-line option. -class ClaspCliConfig::ProgOption : public Potassco::ProgramOptions::Value { -public: - ProgOption(ClaspCliConfig& c, int o) : Potassco::ProgramOptions::Value(0), config_(&c), option_(o) {} - bool doParse(const std::string& opt, const std::string& value) { - int ret = isOption(option_) ? config_->setOption(option_, value.c_str()) : config_->setAppOpt(option_, value.c_str()); - if (ret == -1) { failOption(OptionError::unknown_option, config_->isGenerator() ? "" : "", opt); } - return ret > 0; - } - int option() const { return option_; } -private: - ClaspCliConfig* config_; - int option_; -}; - // Adapter for parsing a command string. struct ClaspCliConfig::ParseContext : public Potassco::ProgramOptions::ParseContext{ typedef Potassco::ProgramOptions::SharedOptPtr OptPtr; - ParseContext(ClaspCliConfig& x, const char* c, const ParsedOpts* ex, bool allowMeta, ParsedOpts* o) - : self(&x), config(c), exclude(ex), out(o), meta(allowMeta) { seen[0] = seen[1] = 0; } + ParseContext(ClaspCliConfig& x, const char* c, const ParsedOpts* ex, uint8 m, uint32 s, ParsedOpts* o) + : self(&x), prev(x.parseCtx_), config(c), exclude(ex), out(o), sId(s), mode(m) { + seen[0] = seen[1] = 0; + x.parseCtx_ = this; + } + ~ParseContext() { self->parseCtx_ = this->prev; } OptPtr getOption(const char* name, FindType ft); OptPtr getOption(int, const char* key) { failOption(OptionError::unknown_option, config, key); } void addValue(const OptPtr& key, const std::string& value); uint64 seen[2]; std::string temp; ClaspCliConfig* self; + ParseContext* prev; const char* config; const ParsedOpts* exclude; ParsedOpts* out; - bool meta; + uint32 sId; + uint8 mode; +}; +class ClaspCliConfig::ProgOption : public Potassco::ProgramOptions::Value { +public: + ProgOption(ClaspCliConfig& c, int o) : Potassco::ProgramOptions::Value(0), config_(&c), option_(o) {} + bool doParse(const std::string& opt, const std::string& value) { + uint8 mode = config_->parseCtx_ ? config_->parseCtx_->mode : 0; + uint32 sId = config_->parseCtx_ ? config_->parseCtx_->sId : 0; + int ret = isOption(option_) ? config_->setOption(option_, mode, sId, value.c_str()) : config_->setAppOpt(option_, mode, value.c_str()); + if (ret == -1) { failOption(OptionError::unknown_option, !isTester(mode) ? "" : "", opt); } + return ret > 0; + } + int option() const { return option_; } +private: + ClaspCliConfig* config_; + int option_; }; void ClaspCliConfig::ParseContext::addValue(const OptPtr& key, const std::string& value) { using namespace Potassco::ProgramOptions; @@ -669,6 +614,7 @@ void ClaspCliConfig::ParseContext::addValue(const OptPtr& key, const std::string Potassco::ProgramOptions::SharedOptPtr ClaspCliConfig::ParseContext::getOption(const char* cmdName, FindType ft) { Options::option_iterator end = self->opts_->end(), it = end; OptionError::Type error = OptionError::unknown_option; + bool meta = (mode & mode_meta) != 0; if (ft == OptionContext::find_alias) { char a = cmdName[*cmdName == '-']; for (it = self->opts_->begin(); it != end && it->get()->alias() != a; ++it) { ; } @@ -676,13 +622,9 @@ Potassco::ProgramOptions::SharedOptPtr ClaspCliConfig::ParseContext::getOption(c else { const char* name = cmdName; if (std::strchr(cmdName, '-') != 0) { cliNameToKey(temp, cmdName); name = temp.c_str(); } - int16 opt = index_g.find(name, (ft & OptionContext::find_prefix) != 0); - if (opt >= 0) { - it = self->opts_->begin() + opt; - } - else if (opt == -2) { - error = OptionError::ambiguous_option; - } + int16 opt = findOption(name, (ft & OptionContext::find_prefix) != 0); + if (opt >= 0) { it = self->opts_->begin() + opt; } + else if (opt == -2) { error = OptionError::ambiguous_option; } assert(it == end || static_cast(it->get()->value())->option() == opt); } if (it != end && (meta || isOption(static_cast(it->get()->value())->option()))) { @@ -693,35 +635,6 @@ Potassco::ProgramOptions::SharedOptPtr ClaspCliConfig::ParseContext::getOption(c ///////////////////////////////////////////////////////////////////////////////////////// // Default Configs ///////////////////////////////////////////////////////////////////////////////////////// -ConfigIter ClaspCliConfig::getConfig(ConfigKey k) { - #define MAKE_CONFIG(n, o1, o2) "/[" n "]\0/\0/" o1 " " o2 "\0" - switch(k) { - #define CONFIG(id, n,c,s,p) case config_##n: return ConfigIter(MAKE_CONFIG(#n, s, c)); - #define CLASP_CLI_DEFAULT_CONFIGS - #define CLASP_CLI_AUX_CONFIGS - #include - case config_many: - #define CONFIG(id,n,c,s,p) MAKE_CONFIG("solver." POTASSCO_STRING(id), c, p) - #define CLASP_CLI_DEFAULT_CONFIGS - #define CLASP_CLI_AUX_CONFIGS - return ConfigIter( - #include - ); - default: POTASSCO_REQUIRE(k == config_default, "Invalid config key '%d'", (int)k); return ConfigIter("/default\0/\0/\0"); - } - #undef MAKE_CONFIG -} -ConfigIter ClaspCliConfig::getConfig(uint8 key, std::string& tempMem) { - POTASSCO_REQUIRE(key <= (config_max_value + 1), "Invalid key!"); - if (key < config_max_value) { return getConfig(static_cast(key)); } - tempMem.clear(); - loadConfig(tempMem, config_[key - config_max_value].c_str()); - return ConfigIter(tempMem.data()); -} -int ClaspCliConfig::getConfigKey(const char* k) { - ConfigKey ret; - return Potassco::string_cast(k, ret) ? ret : -1; -} static inline const char* skipWs(const char* x) { while (*x == ' ' || *x == '\t') { ++x; } return x; @@ -734,11 +647,9 @@ static inline bool matchSep(const char*& x, char c) { if (*(x = skipWs(x)) == c) { ++x; return true; } return false; } - -bool ClaspCliConfig::appendConfig(std::string& to, const std::string& line) { - std::size_t sz = to.size(); - const char* x = skipWs(line.c_str()); - const bool p = matchSep(x, '['); +static bool appendConfig(std::string& to, const std::string& line) { + const char* x = skipWs(line.c_str()); + const bool p = matchSep(x, '['); to.append("/[", 2); // match name in optional square brackets bool ok = matchSep(x = getIdent(x, to), ']') == p; @@ -751,23 +662,48 @@ bool ClaspCliConfig::appendConfig(std::string& to, const std::string& line) { to.append(1, '\0'); return true; } - to.resize(sz); return false; } -bool ClaspCliConfig::loadConfig(std::string& to, const char* name) { +ConfigIter ClaspCliConfig::getConfig(ConfigKey k) { + #define MAKE_CONFIG(n, o1, o2) "/[" n "]\0/\0/" o1 " " o2 "\0" + switch(k) { + #define CONFIG(id, n,c,s,p) case config_##n: return ConfigIter(MAKE_CONFIG(#n, s, c)); + #define CLASP_CLI_DEFAULT_CONFIGS + #define CLASP_CLI_AUX_CONFIGS + #include + case config_many: + #define CONFIG(id,n,c,s,p) MAKE_CONFIG("solver." POTASSCO_STRING(id), c, p) + #define CLASP_CLI_DEFAULT_CONFIGS + #define CLASP_CLI_AUX_CONFIGS + return ConfigIter( + #include + ); + default: POTASSCO_REQUIRE(k == config_default, "Invalid config key '%d'", (int)k); return ConfigIter("/default\0/\0/\0"); + } + #undef MAKE_CONFIG +} +ConfigIter ClaspCliConfig::getConfig(uint8 key, std::string& tempMem) const { + POTASSCO_REQUIRE(key <= (config_max_value + 1), "Invalid key!"); + if (key < config_max_value) { return getConfig(static_cast(key)); } + const char* name = config_[key - config_max_value].c_str(); std::ifstream file(name); POTASSCO_EXPECT(file, "Could not open config file '%s'", name); - uint32 lineNum= 0; + uint32 lineNum = 0; + tempMem.clear(); for (std::string line, cont; std::getline(file, line); ) { ++lineNum; line.erase(0, line.find_first_not_of(" \t")); if (line.empty() || line[0] == '#') { continue; } if (*line.rbegin() == '\\') { *line.rbegin() = ' '; cont += line; continue; } if (!cont.empty()) { cont += line; cont.swap(line); cont.clear(); } - POTASSCO_EXPECT(appendConfig(to, line), "'%s@%u': Invalid configuration", name, lineNum); + POTASSCO_EXPECT(appendConfig(tempMem, line), "'%s@%u': Invalid configuration", name, lineNum); } - to.append(1, '\0'); - return true; + tempMem.append(1, '\0'); + return ConfigIter(tempMem.data()); +} +int ClaspCliConfig::getConfigKey(const char* k) { + ConfigKey ret; + return Potassco::string_cast(k, ret) ? ret : -1; } const char* ClaspCliConfig::getDefaults(ProblemType t) { if (t == Problem_t::Asp){ return "--configuration=tweety"; } @@ -786,13 +722,7 @@ bool ConfigIter::next() { ///////////////////////////////////////////////////////////////////////////////////////// // ClaspCliConfig ///////////////////////////////////////////////////////////////////////////////////////// -ClaspCliConfig::ScopedSet::ScopedSet(ClaspCliConfig& s, uint8 mode, uint32 sId) : self(&s) { - s.mode_ = mode; - s.solver_ = static_cast(sId); -} -ClaspCliConfig::ScopedSet::~ScopedSet() { self->mode_ = self->solver_ = 0; } -ClaspCliConfig::ClaspCliConfig() : mode_(0), solver_(0) { - initTester_ = true; +ClaspCliConfig::ClaspCliConfig() : parseCtx_(0), validate_(false) { static_assert( (option_category_context_begin< option_category_solver_begin) && (option_category_solver_begin < option_category_search_begin) && @@ -801,27 +731,25 @@ ClaspCliConfig::ClaspCliConfig() : mode_(0), solver_(0) { (option_category_solve_begin < option_category_solve_end), "unexpected option order"); } ClaspCliConfig::~ClaspCliConfig() {} -bool ClaspCliConfig::isGenerator() const { return (mode_ & mode_tester) == 0; } void ClaspCliConfig::reset() { config_[0] = config_[1] = ""; - initTester_ = true; + validate_ = false; ClaspConfig::reset(); } -BasicSatConfig* ClaspCliConfig::active() { - return isGenerator() ? this : testerConfig(); -} void ClaspCliConfig::prepare(SharedContext& ctx) { if (testerConfig()) { // Force init ClaspCliConfig::config("tester"); } + if (validate_) { + ClaspCliConfig::validate(); + } ClaspConfig::prepare(ctx); } Configuration* ClaspCliConfig::config(const char* n) { if (n && std::strcmp(n, "tester") == 0) { - if (!testerConfig() || (!testerConfig()->hasConfig && initTester_)) { - setAppOpt(meta_tester, "--config=auto"); - initTester_ = false; + if (!testerConfig()) { + setAppOpt(meta_tester, 0, ""); } return testerConfig(); } @@ -836,12 +764,7 @@ void ClaspCliConfig::createOptions() { using namespace Potassco::ProgramOptions; opts_->addOptions()("configuration", createOption(meta_config)->defaultsTo("auto")->state(Value::value_defaulted), KEY_INIT_DESC("Set default configuration [%D]\n")); std::string cmdName; -#define CLASP_CONTEXT_OPTIONS -#define CLASP_GLOBAL_OPTIONS -#define CLASP_SOLVE_OPTIONS -#define CLASP_ASP_OPTIONS -#define CLASP_SOLVER_OPTIONS -#define CLASP_SEARCH_OPTIONS +#define CLASP_ALL_GROUPS #define OPTION(k, e, a, d, ...) keyToCliName(cmdName, #k, e); opts_->addOptions()(cmdName.c_str(),static_cast( createOption(opt_##k)a ), d); #define ARG(a) ->a #define ARG_EXT(a, X) ARG(a) @@ -899,10 +822,9 @@ ClaspCliConfig::KeyType ClaspCliConfig::getKey(KeyType k, const char* path) cons return k; } if (isLeafId(id)){ return KEY_INVALID; } - const NodeKey& x = nodes_g[-id]; - for (int16 sk = x.skBegin; sk != x.skEnd && sk < 0; ++sk) { - NodeKey sub = nodes_g[-sk]; - if (matchPath(path, sub.name)) { + NodeKey nk = getNode(id); + for (int16 sk = nk.skBeg; sk < 0; ++sk) { + if (matchPath(path, getNode(sk).name)) { KeyType ret = makeKeyHandle(sk, (sk == key_tester ? mode_tester : 0) | decodeMode(k), 0); if (!*path) { return ret; } return getKey(ret, path); @@ -911,14 +833,14 @@ ClaspCliConfig::KeyType ClaspCliConfig::getKey(KeyType k, const char* path) cons uint8 mode = decodeMode(k); if (id == key_solver) { uint32 solverId; - if ((mode & mode_solver) == 0 && *path != '.' && Potassco::xconvert(path, solverId, &path, 0) == 1) { + if (!isSolver(mode) && *path != '.' && Potassco::xconvert(path, solverId, &path, 0) == 1) { return getKey(makeKeyHandle(id, mode | mode_solver, std::min(solverId, (uint32)uint8(-1))), path); } mode |= mode_solver; } - int16 opt = index_g.find(path, false); + int16 opt = findOption(path, false); // remaining name must be a valid option in our subkey range - if (opt < 0 || opt < x.skBegin || opt >= x.skEnd) { + if (opt < 0 || opt < nk.skBeg || opt >= static_cast(nk.skBeg + nk.skSize)) { return KEY_INVALID; } return makeKeyHandle(opt, mode, decodeSolver(k)); @@ -926,7 +848,7 @@ ClaspCliConfig::KeyType ClaspCliConfig::getKey(KeyType k, const char* path) cons ClaspCliConfig::KeyType ClaspCliConfig::getArrKey(KeyType k, unsigned i) const { int16 id = decodeKey(k); - if (id != key_solver || (decodeMode(k) & mode_solver) != 0 || i >= solve.supportedSolvers()) { return KEY_INVALID; } + if (id != key_solver || isSolver(decodeMode(k)) || i >= solve.supportedSolvers()) { return KEY_INVALID; } return makeKeyHandle(id, decodeMode(k) | mode_solver, i); } int ClaspCliConfig::getKeyInfo(KeyType k, int* nSubkeys, int* arrLen, const char** help, int* nValues) const { @@ -936,18 +858,18 @@ int ClaspCliConfig::getKeyInfo(KeyType k, int* nSubkeys, int* arrLen, const char if (isLeafId(id)){ if (nSubkeys && ++ret) { *nSubkeys = 0; } if (arrLen && ++ret) { *arrLen = -1; } - if (nValues && ++ret) { *nValues = static_cast( (decodeMode(k) & mode_tester) == 0 || testerConfig() != 0 ); } + if (nValues && ++ret) { *nValues = static_cast( !isTester(decodeMode(k)) || testerConfig() != 0 ); } if (help && ++ret) { *help = getNode(id).desc; } return ret; } - const NodeKey& x = nodes_g[-id]; - if (nSubkeys && ++ret) { *nSubkeys = x.numSubkeys(); } + NodeKey x = getNode(id); + if (nSubkeys && ++ret) { *nSubkeys = x.skSize; } if (nValues && ++ret) { *nValues = -1; } if (help && ++ret) { *help = x.desc; } if (arrLen && ++ret) { *arrLen = -1; - if (id == key_solver && (decodeMode(k) & mode_solver) == 0) { - const UserConfig* c = (decodeMode(k) & mode_tester) == 0 ? this : testerConfig(); + if (id == key_solver && !isSolver(decodeMode(k))) { + const UserConfig* c = active(this, decodeMode(k)); *arrLen = c ? (int)c->numSolver() : 0; } } @@ -957,26 +879,40 @@ bool ClaspCliConfig::isLeafKey(KeyType k) { return isLeafId(decodeKey(k)); } const char* ClaspCliConfig::getSubkey(KeyType k, uint32 i) const { int16 id = decodeKey(k); if (!isValidId(id) || isLeafId(id)) { return 0; } - const NodeKey& nk = nodes_g[-id]; - if (i >= nk.numSubkeys()) { return 0; } - return getNode(static_cast(nk.skBegin + i)).name; + NodeKey nk = getNode(id); + if (i >= nk.skSize) { return 0; } + return getNode(static_cast(nk.skBeg + i)).name; } int ClaspCliConfig::getValue(KeyType key, std::string& out) const { try { + const UserConfig* base = active(this, decodeMode(key)); int16 o = decodeKey(key); - uint8 m = decodeMode(key); - int ret = -1; - const BasicSatConfig* cfg = m & mode_tester ? testerConfig() : this; - if (isOption(o)) { - ret = execOption(o, const_cast(cfg), m, decodeSolver(key), 0, &out, 0, 0); + int r = isLeafId(o) && base ? 1 : -1; + if (r > 0 && isOption(o)) { + POTASSCO_ASSERT(base == this || isTesterOption(o)); + uint32 sId = decodeSolver(key); + const SolverParams* solver = &base->solver(sId); + const SolveParams* search = &base->search(sId); + // helper macros used in get + using Potassco::toString; using Potassco::off; using Potassco::Set; + #define GET_FUN(x) Potassco::StringRef x(out); if (!x.out);else + #define GET(...) out = toString( __VA_ARGS__ ) + #define GET_IF(c, ...) out = ITE((c), toString(__VA_ARGS__), toString(off)) + switch(static_cast(o)) { + default: POTASSCO_ASSERT(false, "invalid option"); + #define OPTION(k, e, a, h, _, GET) case opt_##k: { GET ; } break; + #define CLASP_ALL_GROUPS + #include + } + #undef GET_FUN + #undef GET + #undef GET_IF } - else if (o == meta_config && cfg) { - uint8 k = (ConfigKey)cfg->cliConfig; - if (k < config_max_value) { xconvert(out, static_cast(k)); } - else { out.append(config_[cfg == testerConfig()]); } - ret = 1; + else if (r > 0 && o == meta_config) { + if (base->cliConfig < config_max_value) { xconvert(out, static_cast(base->cliConfig)); } + else { out.append(config_[base == testerConfig()]); } } - return ret > 0 ? static_cast(out.length()) : ret; + return r > 0 ? static_cast(out.length()) : r; } catch (...) { return -2; } } @@ -1005,30 +941,27 @@ int ClaspCliConfig::setValue(KeyType key, const char* value) { int16 id = decodeKey(key); if (!isLeafId(id)) { return -1; } try { - ScopedSet scope(*this, decodeMode(key), decodeSolver(key)); - if (mode_ & mode_tester) { + uint8 mode = decodeMode(key); + validate_ = true; + if (isTester(mode)) { addTesterConfig(); - initTester_ = false; } if (isOption(id)) { - return setOption(id, value); + return setOption(id, mode, decodeSolver(key), value); } - else if (id == meta_config) { - int sz = setAppOpt(id, value); + else { + int sz = setAppOpt(id, mode, value); if (sz <= 0) { return 0; } std::string m; - UserConfig* act = active(); - ConfigIter it = getConfig(act->cliConfig, m); - act->hasConfig = 0; - mode_ |= mode_relaxed; + UserConfig* act = active(this, mode); + ConfigIter it = getConfig(act->cliConfig, m); + act->hasConfig = 0; + mode |= mode_relaxed; act->resize(1, 1); for (uint32 sId = 0; it.valid(); it.next()) { - act->addSolver(sId); - act->addSearch(sId); - solver_ = static_cast(sId); - if (!setConfig(it, false, ParsedOpts(), 0)){ return 0; } - if (++sId == static_cast(sz)) { break; } - mode_ |= mode_solver; + if (!setConfig(it, mode, sId, ParsedOpts(), 0)){ return 0; } + if (++sId == static_cast(sz)) { break; } + mode |= mode_solver; } if (sz < 65 && static_cast(sz) > act->numSolver()) { for (uint32 sId = act->numSolver(), mod = sId, end = static_cast(sz); sId != end; ++sId) { @@ -1038,10 +971,8 @@ int ClaspCliConfig::setValue(KeyType key, const char* value) { search = act->search(sId % mod); } } - return static_cast((act->hasConfig = 1) == 1); - } - else { - return -1; + act->hasConfig = 1; + return 1; } } catch (...) { @@ -1054,60 +985,92 @@ bool ClaspCliConfig::setValue(const char* path, const char* value) { POTASSCO_REQUIRE(ret >= 0, (ret == -1 ? "Invalid or incomplete key: '%s'" : "Value error in key: '%s'"), path); return ret != 0; } -int ClaspCliConfig::setOption(int option, const char* setVal, const ParsedOpts* exclude) { - std::string m; - if (exclude && exclude->count(getOptionName(option, m))) { - return 1; +int ClaspCliConfig::setOption(int option, uint8 mode_, uint32 sId, const char* _val_) { + if (!_val_ || (isTester(mode_) && !isTesterOption(option)) || (isSolver(mode_) && !isSolverOption(option))) { + return mode_ & mode_relaxed ? 1 : -1; + } + BasicSatConfig* base = active(this, mode_); + SolverParams* solver = isSolverOption(option) ? &base->addSolver(sId) : 0; + SolveParams* search = isSolverOption(option) ? &base->addSearch(sId) : 0; + // action & helper macros used in set + using Potassco::stringTo; using Potassco::opt; using Potassco::Set; + int ret = 1; + try { + #define FUN(x) for (Potassco::ArgString x = _val_;;) + #define STORE(obj) { return stringTo((_val_), obj); } + #define STORE_LEQ(x, y) { unsigned _n; return stringTo(_val_, _n) && SET_LEQ(x, _n, y); } + #define STORE_FLAG(x) { bool _b; return stringTo(_val_, _b) && SET(x, static_cast(_b)); } + #define STORE_OR_FILL(x) { unsigned _n; return stringTo(_val_, _n) && SET_OR_FILL(x, _n); } + #define STORE_U(E, x) { E _e; return stringTo((_val_), _e) && SET(x, static_cast(_e));} + switch (static_cast(option)) { + default: POTASSCO_ASSERT(false, "invalid option"); + #define OPTION(k, e, a, d, SET, ...) case opt_##k: { SET ; } break; + #define CLASP_ALL_GROUPS + #include + } + #undef FUN + #undef STORE + #undef STORE_LEQ + #undef STORE_FLAG + #undef STORE_OR_FILL + #undef STORE_U + } + catch (...) { + ret = 0; } - return execOption(option, active(), mode_, solver_, setVal ? setVal : "", 0, 0, 0); + return ret; } -int ClaspCliConfig::setAppOpt(int o, const char* val) { +int ClaspCliConfig::setAppOpt(int o, uint8 mode, const char* val) { if (o == meta_config) { std::pair defC(config_default, INT_MAX); - if (Potassco::stringTo(val, defC)) { active()->cliConfig = (uint8)defC.first; } + if (Potassco::stringTo(val, defC)) { active(this, mode)->cliConfig = (uint8)defC.first; } else { POTASSCO_EXPECT(std::ifstream(val).is_open(), "Could not open config file '%s'", val); - config_[!isGenerator()] = val; - active()->cliConfig = config_max_value + !isGenerator(); + config_[isTester(mode)] = val; + active(this, mode)->cliConfig = config_max_value + isTester(mode); } return Range(0, INT_MAX).clamp(defC.second); } - else if (o == meta_tester && isGenerator()) { + else if (o == meta_tester && !isTester(mode)) { addTesterConfig(); - initTester_ = false; ParsedOpts ex; - bool ret = ScopedSet(*this, mode_tester)->setConfig("", val, true, ParsedOpts(), &ex); - return ret && finalizeAppConfig(testerConfig(), finalizeParsed(*testerConfig(), ex, ex), Problem_t::Asp, true); + bool ret = setConfig("", val, mode_tester | mode_meta, 0, ParsedOpts(), &ex); + return ret && finalizeAppConfig(mode_tester, finalizeParsed(mode_tester, ex, ex), Problem_t::Asp, true); } return -1; // invalid option } -bool ClaspCliConfig::setAppDefaults(UserConfig* active, ConfigKey config, const ParsedOpts& cmdLine, ProblemType t) { - ScopedSet temp(*this, (active == this ? 0 : mode_tester) | mode_relaxed, 0); - if (t != Problem_t::Asp && !setOption(opt_sat_prepro, "2,iter=20,occ=25,time=120", &cmdLine)) { - return false; +bool ClaspCliConfig::setAppDefaults(ConfigKey config, uint8 mode, const ParsedOpts& seen, ProblemType t) { + std::string mem; + if (t != Problem_t::Asp && !seen.count(getOptionName(opt_sat_prepro, mem))) { + POTASSCO_REQUIRE(setOption(opt_sat_prepro, mode, 0, "2,iter=20,occ=25,time=120")); } - if (t == Problem_t::Asp && config == config_many) { - if (!setOption(opt_eq, "3", &cmdLine)) { return false; } - if (!setOption(opt_trans_ext, "dynamic", &cmdLine)) { return false; } + if (!isTester(mode) && config == config_many && t == Problem_t::Asp) { + POTASSCO_REQUIRE(seen.count(getOptionName(opt_eq, mem)) || setOption(opt_eq, mode, 0, "3")); + POTASSCO_REQUIRE(seen.count(getOptionName(opt_trans_ext, mem)) || setOption(opt_trans_ext, mode, 0, "dynamic")); } - if (active->addSolver(0).search == SolverParams::no_learning) { - if (!setOption(opt_heuristic, "unit", &cmdLine)) { return false; } - if (!setOption(opt_lookahead, "atom", &cmdLine)) { return false; } - if (!setOption(opt_deletion, "no", &cmdLine)) { return false; } - if (!setOption(opt_restarts, "no", &cmdLine)) { return false; } + if (config != config_nolearn && active(this, mode)->solver(0).search == SolverParams::no_learning) { + POTASSCO_REQUIRE(setConfig(getConfig(config_nolearn), mode | mode_relaxed, 0, seen, 0)); } return true; } -bool ClaspCliConfig::setConfig(const char* name, const char* args, bool allowMeta, const ParsedOpts& exclude, ParsedOpts* out) { +bool ClaspCliConfig::setConfig(const char* name, const char* args, uint8 mode, uint32 sId, const ParsedOpts& exclude, ParsedOpts* out) { createOptions(); - ParseContext ctx(*this, name, &exclude, allowMeta, out); + ParseContext ctx(*this, name, &exclude, mode, sId, out); Potassco::ProgramOptions::parseCommandString(args, ctx, Potassco::ProgramOptions::command_line_allow_flag_value); return true; } -bool ClaspCliConfig::setConfig(const ConfigIter& config, bool allowMeta, const ParsedOpts& exclude, ParsedOpts* out) { - return setConfig(config.name(), config.args(), allowMeta, exclude, out); +bool ClaspCliConfig::setConfig(const ConfigIter& config, uint8 mode, uint32 sId, const ParsedOpts& exclude, ParsedOpts* out) { + if (*config.base()) { + ConfigKey baseK = config_default; + POTASSCO_REQUIRE(Potassco::stringTo(config.base(), baseK), "%s: '%s': Invalid base config!", config.name(), config.base()); + ConfigIter base = getConfig(baseK); + if (!setConfig(base.name(), base.args(), mode | mode_solver, sId, exclude, out)) { + return false; + } + } + return setConfig(config.name(), config.args(), mode, sId, exclude, out); } bool ClaspCliConfig::validate() { UserConfiguration* arr[3] = { this, testerConfig(), 0 }; @@ -1119,16 +1082,18 @@ bool ClaspCliConfig::validate() { POTASSCO_REQUIRE((err = Clasp::Cli::validate((*c)->solver(i), (*c)->search(i))) == 0, "<%s>.%u: %s", ctx, i, err); } } while (*++c); + validate_ = false; return true; } bool ClaspCliConfig::finalize(const ParsedOpts& x, ProblemType t, bool defs) { ParsedOpts temp; - return finalizeAppConfig(this, finalizeParsed(*this, x, temp), t, defs); + return finalizeAppConfig(0, finalizeParsed(0, x, temp), t, defs) + && finalizeAppConfig(mode_tester, ParsedOpts(), Problem_t::Asp, true); } void ClaspCliConfig::addDisabled(ParsedOpts& parsed) { - finalizeParsed(*this, parsed, parsed); + finalizeParsed(0, parsed, parsed); } const std::string& ClaspCliConfig::getOptionName(int o, std::string& mem) const { @@ -1140,10 +1105,10 @@ const std::string& ClaspCliConfig::getOptionName(int o, std::string& mem) const return mem; } -const ClaspCliConfig::ParsedOpts& ClaspCliConfig::finalizeParsed(UserConfig& active, const ParsedOpts& parsed, ParsedOpts& exclude) const { +const ClaspCliConfig::ParsedOpts& ClaspCliConfig::finalizeParsed(uint8 mode, const ParsedOpts& parsed, ParsedOpts& exclude) const { const ParsedOpts* ret = &parsed; std::string mem; - if (active.search(0).reduce.fReduce() == 0 && parsed.count(getOptionName(opt_deletion, mem)) != 0) { + if (active(this, mode)->search(0).reduce.fReduce() == 0 && parsed.count(getOptionName(opt_deletion, mem)) != 0) { if (ret != &exclude) { exclude = parsed; } exclude.add(getOptionName(opt_del_cfl, mem)); exclude.add(getOptionName(opt_del_max, mem)); @@ -1153,48 +1118,42 @@ const ClaspCliConfig::ParsedOpts& ClaspCliConfig::finalizeParsed(UserConfig& act return *ret; } -bool ClaspCliConfig::finalizeAppConfig(UserConfig* active, const ParsedOpts& parsed, ProblemType t, bool defs) { - if (!active || active->hasConfig) { return true; } - SolverParams defSolver = active->solver(0); - SolveParams defSearch = active->search(0); - uint8 c = active->cliConfig; +bool ClaspCliConfig::finalizeAppConfig(uint8 mode, const ParsedOpts& parsed, ProblemType t, bool defs) { + UserConfig* config = active(this, mode); + if (!config || config->hasConfig) { return true; } + SolverParams defSolver = config->solver(0); + SolveParams defSearch = config->search(0); + uint8 c = config->cliConfig; if (c == config_many && solve.numSolver() == 1) { c = config_default; } if (c == config_default) { if (defSolver.search == SolverParams::no_learning) { c = config_nolearn; } - else if (active == testerConfig()) { c = config_tester_default; } + else if (isTester(mode)) { c = config_tester_default; } else if (solve.numSolver() == 1 || !solve.defaultPortfolio()) { c = t == Problem_t::Asp ? (uint8)config_asp_default : (uint8)config_sat_default; } else { c = config_many; } } - if (defs && !setAppDefaults(active, static_cast(c), parsed, t)) { return false; } + if (defs && !setAppDefaults(static_cast(c), mode, parsed, t)) { return false; } std::string m; ConfigIter conf = getConfig(c, m); - uint8 mode = (active == testerConfig() ? mode_tester : 0) | mode_relaxed; - uint32 portSize = 0; - const char* ctx = active == testerConfig() ? "tester" : "config", *err = 0; + mode |= mode_relaxed; + const char* ctx = isTester(mode) ? "tester" : "config", *err = 0; for (uint32 i = 0; i != solve.numSolver() && conf.valid(); ++i) { - SolverParams& solver = (active->addSolver(i) = defSolver).setId(i); - SolveParams& search = (active->addSearch(i) = defSearch); - ConfigKey baseK = config_default; - POTASSCO_REQUIRE(!*conf.base() || Potassco::stringTo(conf.base(), baseK), "<%s>.%s: '%s': Invalid base config!", ctx, conf.name(), conf.base()); - if (baseK != config_default && !ScopedSet(*this, mode|mode_solver, i)->setConfig(getConfig(baseK), false, parsed, 0)) { - return false; - } - if (!ScopedSet(*this, mode, i)->setConfig(conf, false, parsed, 0)) { + SolverParams& solver = (config->addSolver(i) = defSolver).setId(i); + SolveParams& search = (config->addSearch(i) = defSearch); + if (!setConfig(conf, mode, i, parsed, 0)) { return false; } POTASSCO_REQUIRE((err = Clasp::Cli::validate(solver, search)) == 0, "<%s>.%s : %s", ctx, conf.name(), err); - ++portSize; conf.next(); mode |= mode_solver; } - active->hasConfig = 1; + config->hasConfig = 1; return true; } bool ClaspCliConfig::setAppConfig(const std::string& args, ProblemType t) { Potassco::ProgramOptions::ParsedOptions exclude; reset(); - return setConfig("setConfig", args.c_str(), true, exclude, &exclude) && assignDefaults(exclude) && finalize(exclude, t, true); + return setConfig("setConfig", args.c_str(), mode_meta, 0, exclude, &exclude) && assignDefaults(exclude) && finalize(exclude, t, true); } const char* validate(const SolverParams& solver, const SolveParams& search) { diff --git a/tests/cli_test.cpp b/tests/cli_test.cpp index 36a20a2..7eea228 100644 --- a/tests/cli_test.cpp +++ b/tests/cli_test.cpp @@ -104,6 +104,7 @@ TEST_CASE("Cli options", "[cli]") { REQUIRE(config.testerConfig() == 0); REQUIRE(config.setValue("tester.configuration", "tweety")); REQUIRE(config.testerConfig() != 0); + REQUIRE(config.testerConfig()->hasConfig); config.getKeyInfo(initTest, 0, 0, 0, &nVal); REQUIRE(nVal == 1); @@ -117,7 +118,7 @@ TEST_CASE("Cli options", "[cli]") { const char* tempName = ".test_testConfigInitFromFile.port"; std::ofstream temp(tempName); temp << "# A test config" << std::endl; - temp << "[t0]: --models=0 " << " --heuristic=Berkmin --restarts=x,100,1.5\n"; + temp << "[t0]: --models=0 --heuristic=Berkmin --restarts=x,100,1.5\n"; temp.close(); config.setValue("configuration", tempName); @@ -128,12 +129,42 @@ TEST_CASE("Cli options", "[cli]") { std::remove(tempName); REQUIRE(config.setValue(config.getKey(ClaspCliConfig::KEY_ROOT, "configuration"), tempName) == -2); } + SECTION("test init from file applies base") { + const char* tempName = ".test_testConfigInitFromFile.port"; + std::ofstream temp(tempName); + temp << "# A test config" << std::endl; + SECTION("valid") { + temp << "[t0](trendy): --models=0 --heuristic=Berkmin\n"; + temp.close(); + REQUIRE(config.getValue("solver.otfs") == "0"); + config.setValue("configuration", tempName); + REQUIRE(config.getValue("configuration") == tempName); + CHECK(config.getValue("solver.otfs") == "2"); + } + SECTION("invalid") { + temp << "[t0](invalidBase): --models=0 --heuristic=Berkmin --restarts=x,100,1.5\n"; + temp.close(); + CHECK_THROWS_AS(config.setValue("configuration", tempName), std::logic_error); + } + std::remove(tempName); + } SECTION("test init with invalid file") { const char* tempName = ".test_testConfigInitInvalidOptionInCmdString.port"; std::ofstream temp(tempName); - temp << "[fail]: --config=many" << std::endl; - temp.close(); - REQUIRE_THROWS_AS(config.setValue("configuration", tempName), std::logic_error); + SECTION("invalid option") { + temp << "[fail]: --config=many" << std::endl; + temp.close(); + CHECK_THROWS_AS(config.setValue("configuration", tempName), std::logic_error); + CHECK(config.validate()); + } + SECTION("invalid config") { + temp << "[fail]: --no-lookback --heuristic=Berkmin" << std::endl; + temp.close(); + CHECK(config.setValue("configuration", tempName)); + CHECK_THROWS_AS(config.validate(), std::logic_error); + SharedContext ctx; + CHECK_THROWS_AS(config.prepare(ctx), std::logic_error); + } std::remove(tempName); } SECTION("test init ignore deletion if disabled") { @@ -176,6 +207,8 @@ TEST_CASE("Cli options", "[cli]") { SECTION("test master solver is implicit") { REQUIRE(config.getValue("solver.heuristic") == "auto,0"); REQUIRE((config.setValue("solver.heuristic", "berkmin") && config.solver(0).heuId == Heuristic_t::Berkmin)); + REQUIRE_FALSE(config.hasConfig); + REQUIRE(config.getValue("configuration") == "auto"); } SECTION("test solver is implicitly created") { // solver option @@ -211,6 +244,12 @@ TEST_CASE("Cli options", "[cli]") { SECTION("test tester is implicitly created") { REQUIRE(config.setValue("tester.learn_explicit", "1")); REQUIRE((config.testerConfig() != 0 && config.testerConfig()->shortMode == 1)); + REQUIRE_FALSE(config.testerConfig()->hasConfig); + REQUIRE(config.getValue("tester.configuration") == "auto"); + REQUIRE(config.testerConfig()->satPre.type == 0); + REQUIRE(config.config("tester")); + REQUIRE(config.testerConfig()->satPre.type == 0); + REQUIRE_FALSE(config.testerConfig()->hasConfig); } SECTION("test keys") { @@ -574,6 +613,7 @@ TEST_CASE("Cli mt options", "[cli][mt]") { } REQUIRE(config.testerConfig()); REQUIRE(config.testerConfig()->numSolver() == 1); + REQUIRE(config.testerConfig()->hasConfig); REQUIRE(config.getValue("tester.configuration") == "frumpy"); } SECTION("test init from file") {