Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SettingsManager.cpp
Go to the documentation of this file.
2
3#include <boost/algorithm/string.hpp>
4#include <boost/io/ios_state.hpp>
5#include <cstring>
6#include <fstream>
7#include <iomanip>
8#include <iostream>
9#include <mutex>
10#include <regex>
11#include <set>
12
15#include "storm/io/file.h"
48
49namespace storm {
50namespace settings {
51
52SettingsManager::SettingsManager() : modules(), longNameToOptions(), shortNameToOptions(), moduleOptions() {}
53
54SettingsManager::~SettingsManager() {
55 // Intentionally left empty.
56}
57
59 static SettingsManager settingsManager;
60 return settingsManager;
61}
62
63void SettingsManager::setName(std::string const& name, std::string const& executableName) {
64 this->name = name;
65 this->executableName = executableName;
66}
67
68void SettingsManager::setFromCommandLine(int const argc, char const* const argv[]) {
69 // We convert the arguments to a vector of strings and strip off the first element since it refers to the
70 // name of the program.
71 std::vector<std::string> argumentVector(argc - 1);
72 for (int i = 1; i < argc; ++i) {
73 argumentVector[i - 1] = std::string(argv[i]);
74 }
75
76 this->setFromExplodedString(argumentVector);
77}
78
79void SettingsManager::setFromString(std::string const& commandLineString) {
80 if (commandLineString.empty()) {
81 this->setFromExplodedString({});
82 } else {
83 std::vector<std::string> argumentVector;
84 boost::split(argumentVector, commandLineString, boost::is_any_of("\t "));
85 this->setFromExplodedString(argumentVector);
86 }
87}
88
89void SettingsManager::handleUnknownOption(std::string const& optionName, bool isShort) const {
90 std::string optionNameWithDashes = (isShort ? "-" : "--") + optionName;
91 storm::utility::string::SimilarStrings similarStrings(optionNameWithDashes, 0.6, false);
92 std::map<std::string, std::vector<std::string>> similarOptionNames;
93 for (auto const& longOption : longNameToOptions) {
94 if (similarStrings.add("--" + longOption.first)) {
95 similarOptionNames["--" + longOption.first].push_back(longOption.first);
96 }
97 }
98 for (auto const& shortOption : shortNameToOptions) {
99 if (similarStrings.add("-" + shortOption.first)) {
100 for (auto const& option : shortOption.second) {
101 similarOptionNames["-" + shortOption.first].push_back(option->getLongName());
102 }
103 }
104 }
105 std::string errorMessage = "Unknown option '" + optionNameWithDashes + "'.";
106 if (!similarOptionNames.empty()) {
107 errorMessage += " " + similarStrings.toDidYouMeanString() + "\n\n";
108 std::vector<std::string> sortedSimilarOptionNames;
109 auto similarStringsList = similarStrings.toList();
110 for (auto const& s : similarStringsList) {
111 for (auto const& longOptionName : similarOptionNames.at(s)) {
112 sortedSimilarOptionNames.push_back(longOptionName);
113 }
114 }
115 errorMessage += getHelpForSelection({}, sortedSimilarOptionNames, "", "##### Suggested options:");
116 }
117 STORM_LOG_THROW(false, storm::exceptions::OptionParserException, errorMessage);
118}
119
120void SettingsManager::setFromExplodedString(std::vector<std::string> const& commandLineArguments) {
121 // In order to assign the parsed arguments to an option, we need to keep track of the "active" option's name.
122 bool optionActive = false;
123 bool activeOptionIsShortName = false;
124 std::string activeOptionName = "";
125 std::vector<std::string> argumentCache;
126
127 // Walk through all arguments.
128 for (uint_fast64_t i = 0; i < commandLineArguments.size(); ++i) {
129 std::string const& currentArgument = commandLineArguments[i];
130
131 // Check if the given argument is a new option or belongs to a previously given option. An argument that
132 // starts with '-' is normally considered a new option. However, if an option is currently active and still
133 // expects a mandatory (non-optional) argument, a leading '-' may also be part of the argument's value,
134 // e.g. for negative numbers in a region description.
135 bool isNewOption = !currentArgument.empty() && currentArgument.at(0) == '-';
136 if (isNewOption && optionActive) {
137 auto const& activeOptionMap = activeOptionIsShortName ? shortNameToOptions : longNameToOptions;
138 auto activeOptionIterator = activeOptionMap.find(activeOptionName);
139 bool activeOptionExpectsMandatoryArgument = activeOptionIterator != activeOptionMap.end() && !activeOptionIterator->second.empty() &&
140 argumentCache.size() < activeOptionIterator->second.front()->getArgumentCount() &&
141 !activeOptionIterator->second.front()->getArgument(argumentCache.size()).getIsOptional();
142 bool currentArgumentIsKnownOption = currentArgument.size() > 1 && currentArgument.at(1) == '-'
143 ? longNameToOptions.find(currentArgument.substr(2)) != longNameToOptions.end()
144 : shortNameToOptions.find(currentArgument.substr(1)) != shortNameToOptions.end();
145 if (activeOptionExpectsMandatoryArgument && !currentArgumentIsKnownOption) {
146 isNewOption = false;
147 }
148 }
149
150 if (isNewOption) {
151 if (optionActive) {
152 // At this point we know that a new option is about to come. Hence, we need to assign the current
153 // cache content to the option that was active until now.
154 setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache);
155
156 // After the assignment, the argument cache needs to be cleared.
157 argumentCache.clear();
158 } else {
159 optionActive = true;
160 }
161
162 if (currentArgument.size() > 1 && currentArgument.at(1) == '-') {
163 // In this case, the argument has to be the long name of an option. Try to get all options that
164 // match the long name.
165 std::string optionName = currentArgument.substr(2);
166 auto optionIterator = this->longNameToOptions.find(optionName);
167 if (optionIterator == this->longNameToOptions.end()) {
168 handleUnknownOption(optionName, false);
169 }
170 activeOptionIsShortName = false;
171 activeOptionName = optionName;
172 } else {
173 // In this case, the argument has to be the short name of an option. Try to get all options that
174 // match the short name.
175 std::string optionName = currentArgument.substr(1);
176 auto optionIterator = this->shortNameToOptions.find(optionName);
177 if (optionIterator == this->shortNameToOptions.end()) {
178 handleUnknownOption(optionName, true);
179 }
180 activeOptionIsShortName = true;
181 activeOptionName = optionName;
182 }
183 } else if (optionActive) {
184 // Add the current argument to the list of arguments for the currently active options.
185 argumentCache.push_back(currentArgument);
186 } else {
187 STORM_LOG_THROW(false, storm::exceptions::OptionParserException,
188 "Found stray argument '" << currentArgument << "' that is not preceeded by a matching option.");
189 }
190 }
191
192 // If an option is still active at this point, we need to set it.
193 if (optionActive) {
194 setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache);
195 }
196
197 // Include the options from a possibly specified configuration file, but don't overwrite existing settings.
201 }
202
203 // Finally, check whether all modules are okay with the current settings.
204 this->finalizeAllModules();
205}
206
207void SettingsManager::setFromConfigurationFile(std::string const& configFilename) {
208 std::map<std::string, std::vector<std::string>> configurationFileSettings = parseConfigFile(configFilename);
209
210 for (auto const& optionArgumentsPair : configurationFileSettings) {
211 auto options = this->longNameToOptions.find(optionArgumentsPair.first);
212
213 // We don't need to check whether this option exists or not, because this is already checked when
214 // parsing the configuration file.
215
216 // Now go through all the matching options and set them according to the values.
217 for (auto option : options->second) {
218 if (option->getHasOptionBeenSet()) {
219 // If the option was already set from the command line, we issue a warning and ignore the
220 // settings from the configuration file.
221 STORM_LOG_WARN("The option '" << option->getLongName() << "' of module '" << option->getModuleName()
222 << "' has been set in the configuration file '" << configFilename
223 << "', but was overwritten on the command line.\n");
224 } else {
225 // If, however, the option has not been set yet, we try to assign values ot its arguments
226 // based on the argument strings.
227 setOptionArguments(optionArgumentsPair.first, option, optionArgumentsPair.second);
228 }
229 }
230 }
231 // Finally, check whether all modules are okay with the current settings.
232 this->finalizeAllModules();
233}
234
235void SettingsManager::printHelp(std::string const& filter) const {
236 std::cout << "usage: " << executableName << " [options]\n\n";
237
238 if (filter == "frequent" || filter == "all") {
239 bool includeAdvanced = (filter == "all");
240 // Find longest option name.
241 uint_fast64_t maxLength = getPrintLengthOfLongestOption(includeAdvanced);
242
243 std::vector<std::string> invisibleModules;
244 uint64_t numHidden = 0;
245 for (auto const& moduleName : this->moduleNames) {
246 // Only print for visible modules.
247 if (hasModule(moduleName, true)) {
248 std::cout << getHelpForModule(moduleName, maxLength, includeAdvanced);
249 // collect 'hidden' options
250 if (!includeAdvanced) {
251 auto moduleIterator = moduleOptions.find(moduleName);
252 if (moduleIterator != this->moduleOptions.end()) {
253 bool allAdvanced = true;
254 for (auto const& option : moduleIterator->second) {
255 if (!option->getIsAdvanced()) {
256 allAdvanced = false;
257 } else {
258 ++numHidden;
259 }
260 }
261 if (!moduleIterator->second.empty() && allAdvanced) {
262 invisibleModules.push_back(moduleName);
263 }
264 }
265 }
266 }
267 }
268 if (!includeAdvanced) {
269 if (numHidden == 1) {
270 std::cout << numHidden << " hidden option.\n";
271 } else {
272 std::cout << numHidden << " hidden options.\n";
273 }
274 if (!invisibleModules.empty()) {
275 if (invisibleModules.size() == 1) {
276 std::cout << invisibleModules.size() << " hidden module (" << boost::join(invisibleModules, ", ") << ").\n";
277 } else {
278 std::cout << invisibleModules.size() << " hidden modules (" << boost::join(invisibleModules, ", ") << ").\n";
279 }
280 }
281 std::cout << "\nType '" + executableName + " --help modulename' to display all options of a specific module.\n";
282 std::cout << "Type '" + executableName + " --help all' to display a complete list of options.\n";
283 }
284 } else {
285 // Create a regular expression from the input hint.
286 std::regex hintRegex(filter, std::regex_constants::ECMAScript | std::regex_constants::icase);
287
288 // Try to match the regular expression against the known modules.
289 std::vector<std::string> matchingModuleNames;
290 for (auto const& moduleName : this->moduleNames) {
291 if (std::regex_search(moduleName, hintRegex)) {
292 if (hasModule(moduleName, true)) {
293 matchingModuleNames.push_back(moduleName);
294 }
295 }
296 }
297
298 // Try to match the regular expression against the known options.
299 std::vector<std::string> matchingOptionNames;
300 for (auto const& optionName : this->longOptionNames) {
301 if (std::regex_search(optionName, hintRegex)) {
302 matchingOptionNames.push_back(optionName);
303 }
304 }
305
306 std::string optionList = getHelpForSelection(matchingModuleNames, matchingOptionNames,
307 "Matching modules for filter '" + filter + "':", "Matching options for filter '" + filter + "':");
308 if (optionList.empty()) {
309 std::cout << "Filter '" << filter << "' did not match any modules or options.\n";
310 } else {
311 std::cout << optionList;
312 }
313 }
314}
315
316std::string SettingsManager::getHelpForSelection(std::vector<std::string> const& selectedModuleNames, std::vector<std::string> const& selectedLongOptionNames,
317 std::string modulesHeader, std::string optionsHeader) const {
318 std::stringstream stream;
319
320 // Remember which options we printed, so we don't display options twice.
321 std::set<std::shared_ptr<Option>> printedOptions;
322
323 // Try to match the regular expression against the known modules.
324 uint_fast64_t maxLengthModules = 0;
325 for (auto const& moduleName : selectedModuleNames) {
326 maxLengthModules = std::max(maxLengthModules, getPrintLengthOfLongestOption(moduleName, true));
327 // Add all options of this module to the list of printed options so we don't print them twice.
328 auto optionIterator = this->moduleOptions.find(moduleName);
329 STORM_LOG_ASSERT(optionIterator != this->moduleOptions.end(), "Unable to find selected module " << moduleName << ".");
330 printedOptions.insert(optionIterator->second.begin(), optionIterator->second.end());
331 }
332
333 // Try to match the regular expression against the known options.
334 std::vector<std::shared_ptr<Option>> matchingOptions;
335 uint_fast64_t maxLengthOptions = 0;
336 for (auto const& optionName : selectedLongOptionNames) {
337 auto optionIterator = this->longNameToOptions.find(optionName);
338 STORM_LOG_ASSERT(optionIterator != this->longNameToOptions.end(), "Unable to find selected option " << optionName << ".");
339 for (auto const& option : optionIterator->second) {
340 // Only add the option if we have not already added it to the list of options that is going
341 // to be printed anyway.
342 if (printedOptions.find(option) == printedOptions.end()) {
343 maxLengthOptions = std::max(maxLengthOptions, option->getPrintLength());
344 matchingOptions.push_back(option);
345 printedOptions.insert(option);
346 }
347 }
348 }
349
350 // Print the matching modules.
351 uint_fast64_t maxLength = std::max(maxLengthModules, maxLengthOptions);
352 if (selectedModuleNames.size() > 0) {
353 if (modulesHeader != "") {
354 stream << modulesHeader << '\n';
355 }
356 for (auto const& matchingModuleName : selectedModuleNames) {
357 stream << getHelpForModule(matchingModuleName, maxLength, true);
358 }
359 }
360
361 // Print the matching options.
362 if (matchingOptions.size() > 0) {
363 if (optionsHeader != "") {
364 stream << optionsHeader << '\n';
365 }
366 for (auto const& option : matchingOptions) {
367 stream << std::setw(maxLength) << std::left << *option << '\n';
368 }
369 }
370 return stream.str();
371}
372
373std::string SettingsManager::getHelpForModule(std::string const& moduleName, uint_fast64_t maxLength, bool includeAdvanced) const {
374 auto moduleIterator = moduleOptions.find(moduleName);
375 if (moduleIterator == this->moduleOptions.end()) {
376 return "";
377 }
378 // STORM_LOG_THROW(moduleIterator != moduleOptions.end(), storm::exceptions::IllegalFunctionCallException, "Cannot print help for unknown module '" <<
379 // moduleName << "'.");
380
381 // Check whether there is at least one (enabled) option in this module
382 uint64_t numOfOptions = 0;
383 for (auto const& option : moduleIterator->second) {
384 if (includeAdvanced || !option->getIsAdvanced()) {
385 ++numOfOptions;
386 }
387 }
388
389 std::stringstream stream;
390 if (numOfOptions > 0) {
391 std::string displayedModuleName = "'" + moduleName + "'";
392 if (!includeAdvanced) {
393 displayedModuleName += " (" + std::to_string(numOfOptions) + "/" + std::to_string(moduleIterator->second.size()) + " shown)";
394 }
395 stream << "##### Module " << displayedModuleName << " " << std::string(std::min(maxLength, maxLength - displayedModuleName.length() - 14), '#') << '\n';
396
397 // Save the flags for std::cout so we can manipulate them and be sure they will be restored as soon as this
398 // stream goes out of scope.
399 boost::io::ios_flags_saver out(std::cout);
400
401 for (auto const& option : moduleIterator->second) {
402 if (includeAdvanced || !option->getIsAdvanced()) {
403 stream << std::setw(maxLength) << std::left << *option << '\n';
404 }
405 }
406 stream << '\n';
407 }
408 return stream.str();
409}
410
411uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(bool includeAdvanced) const {
412 uint_fast64_t length = 0;
413 for (auto const& moduleName : this->moduleNames) {
414 length = std::max(getPrintLengthOfLongestOption(moduleName, includeAdvanced), length);
415 }
416 return length;
417}
418
419uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(std::string const& moduleName, bool includeAdvanced) const {
420 auto moduleIterator = modules.find(moduleName);
421 STORM_LOG_THROW(moduleIterator != modules.end(), storm::exceptions::IllegalFunctionCallException,
422 "Unable to retrieve option length of unknown module '" << moduleName << "'.");
423 return moduleIterator->second->getPrintLengthOfLongestOption(includeAdvanced);
424}
425
426void SettingsManager::addModule(std::unique_ptr<modules::ModuleSettings>&& moduleSettings, bool doRegister) {
427 auto moduleIterator = this->modules.find(moduleSettings->getModuleName());
428 STORM_LOG_THROW(moduleIterator == this->modules.end(), storm::exceptions::IllegalFunctionCallException,
429 "Unable to register module '" << moduleSettings->getModuleName() << "' because a module with the same name already exists.");
430
431 // Take over the module settings object.
432 std::string moduleName = moduleSettings->getModuleName();
433 this->moduleNames.push_back(moduleName);
434 this->modules.emplace(moduleSettings->getModuleName(), std::move(moduleSettings));
435 auto iterator = this->modules.find(moduleName);
436 std::unique_ptr<modules::ModuleSettings> const& settings = iterator->second;
437
438 if (doRegister) {
439 this->moduleOptions.emplace(moduleName, std::vector<std::shared_ptr<Option>>());
440 // Now register the options of the module.
441 for (auto const& option : settings->getOptions()) {
442 this->addOption(option);
443 }
444 }
445}
446
447void SettingsManager::addOption(std::shared_ptr<Option> const& option) {
448 // First, we register to which module the given option belongs.
449 auto moduleOptionIterator = this->moduleOptions.find(option->getModuleName());
450 STORM_LOG_THROW(moduleOptionIterator != this->moduleOptions.end(), storm::exceptions::IllegalFunctionCallException,
451 "Cannot add option for unknown module '" << option->getModuleName() << "'.");
452 moduleOptionIterator->second.emplace_back(option);
453
454 // Then, we add the option's name (and possibly short name) to the registered options. If a module prefix is
455 // not required for this option, we have to add both versions to our mappings, the prefixed one and the
456 // non-prefixed one.
457 if (!option->getRequiresModulePrefix()) {
458 bool isCompatible = storm::settings::SettingsManager::isCompatible(option, option->getLongName(), this->longNameToOptions);
459 STORM_LOG_THROW(isCompatible, storm::exceptions::IllegalFunctionCallException,
460 "Unable to add option '" << option->getLongName() << "', because an option with the same name is incompatible with it.");
461 addOptionToMap(option->getLongName(), option, this->longNameToOptions);
462 }
463 // For the prefixed name, we don't need a compatibility check, because a module is not allowed to register the same option twice.
464 addOptionToMap(option->getModuleName() + ":" + option->getLongName(), option, this->longNameToOptions);
465 longOptionNames.push_back(option->getModuleName() + ":" + option->getLongName());
466
467 if (option->getHasShortName()) {
468 if (!option->getRequiresModulePrefix()) {
469 bool isCompatible = storm::settings::SettingsManager::isCompatible(option, option->getShortName(), this->shortNameToOptions);
470 STORM_LOG_THROW(isCompatible, storm::exceptions::IllegalFunctionCallException,
471 "Unable to add option '" << option->getLongName() << "', because an option with the same name is incompatible with it.");
472 addOptionToMap(option->getShortName(), option, this->shortNameToOptions);
473 }
474 addOptionToMap(option->getModuleName() + ":" + option->getShortName(), option, this->shortNameToOptions);
475 }
476}
477
478bool SettingsManager::hasModule(std::string const& moduleName, bool checkHidden) const {
479 if (checkHidden) {
480 return this->moduleOptions.find(moduleName) != this->moduleOptions.end();
481 } else {
482 return this->modules.find(moduleName) != this->modules.end();
483 }
484}
485
486modules::ModuleSettings const& SettingsManager::getModule(std::string const& moduleName) const {
487 auto moduleIterator = this->modules.find(moduleName);
488 STORM_LOG_THROW(moduleIterator != this->modules.end(), storm::exceptions::IllegalFunctionCallException,
489 "Cannot retrieve unknown module '" << moduleName << "'.");
490 return *moduleIterator->second;
491}
492
494 auto moduleIterator = this->modules.find(moduleName);
495 STORM_LOG_THROW(moduleIterator != this->modules.end(), storm::exceptions::IllegalFunctionCallException,
496 "Cannot retrieve unknown module '" << moduleName << "'.");
497 return *moduleIterator->second;
498}
499
500bool SettingsManager::isCompatible(std::shared_ptr<Option> const& option, std::string const& optionName,
501 std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>> const& optionMap) {
502 auto optionIterator = optionMap.find(optionName);
503 if (optionIterator != optionMap.end()) {
504 for (auto const& otherOption : optionIterator->second) {
505 bool locallyCompatible = option->isCompatibleWith(*otherOption);
506 if (!locallyCompatible) {
507 return false;
508 }
509 }
510 }
511 return true;
512}
513
514void SettingsManager::setOptionArguments(std::string const& optionName, std::shared_ptr<Option> option, std::vector<std::string> const& argumentCache) {
515 STORM_LOG_THROW(argumentCache.size() <= option->getArgumentCount(), storm::exceptions::OptionParserException,
516 "Too many arguments for option '" << optionName << "'.");
517 STORM_LOG_THROW(!option->getHasOptionBeenSet(), storm::exceptions::OptionParserException, "Option '" << optionName << "' is set multiple times.");
518
519 // Now set the provided argument values one by one.
520 for (uint_fast64_t i = 0; i < argumentCache.size(); ++i) {
521 ArgumentBase& argument = option->getArgument(i);
522 bool conversionOk = argument.setFromStringValue(argumentCache[i]);
523 STORM_LOG_THROW(conversionOk, storm::exceptions::OptionParserException,
524 "Value '" << argumentCache[i] << "' is invalid for argument <" << argument.getName() << "> of option:\n"
525 << *option << ".");
526 }
527
528 // In case there are optional arguments that were not set, we set them to their default value.
529 for (uint_fast64_t i = argumentCache.size(); i < option->getArgumentCount(); ++i) {
530 ArgumentBase& argument = option->getArgument(i);
531 STORM_LOG_THROW(argument.getIsOptional(), storm::exceptions::OptionParserException,
532 "Non-optional argument <" << argument.getName() << "> of option:\n"
533 << *option << ".");
534 argument.setFromDefaultValue();
535 }
536
537 option->setHasOptionBeenSet();
538 if (optionName != option->getLongName() && optionName != option->getShortName() && boost::starts_with(optionName, option->getModuleName())) {
539 option->setHasOptionBeenSetWithModulePrefix();
540 }
541}
542
543void SettingsManager::setOptionsArguments(std::string const& optionName, std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>> const& optionMap,
544 std::vector<std::string> const& argumentCache) {
545 auto optionIterator = optionMap.find(optionName);
546 STORM_LOG_THROW(optionIterator != optionMap.end(), storm::exceptions::OptionParserException, "Unknown option '" << optionName << "'.");
547
548 // Iterate over all options and set the arguments.
549 for (auto& option : optionIterator->second) {
550 setOptionArguments(optionName, option, argumentCache);
551 }
552}
553
554void SettingsManager::addOptionToMap(std::string const& name, std::shared_ptr<Option> const& option,
555 std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>& optionMap) {
556 auto optionIterator = optionMap.find(name);
557 if (optionIterator == optionMap.end()) {
558 std::vector<std::shared_ptr<Option>> optionVector;
559 optionVector.push_back(option);
560 optionMap.emplace(name, optionVector);
561 } else {
562 optionIterator->second.push_back(option);
563 }
564}
565
566void SettingsManager::finalizeAllModules() {
567 for (auto const& nameModulePair : this->modules) {
568 nameModulePair.second->finalize();
569 nameModulePair.second->check();
570 }
571}
572
573std::map<std::string, std::vector<std::string>> SettingsManager::parseConfigFile(std::string const& filename) const {
574 std::map<std::string, std::vector<std::string>> result;
575
576 std::ifstream input;
577 storm::io::openFile(filename, input);
578
579 bool globalScope = true;
580 std::string activeModule = "";
581 uint_fast64_t lineNumber = 1;
582 for (std::string line; storm::io::getline(input, line); ++lineNumber) {
583 // If the first character of the line is a "[", we expect the settings of a new module to start and
584 // the line to be of the shape [<module>].
585 if (line.at(0) == '[') {
587 line.at(0) == '[' && line.find("]") == line.length() - 1 && line.find("[", 1) == line.npos, storm::exceptions::OptionParserException,
588 "Illegal module name header in configuration file '" << filename << " in line " << std::to_string(lineNumber)
589 << ". Expected [<module>] where <module> is a placeholder for a known module.");
590
591 // Extract the module name and check whether it's a legal one.
592 std::string moduleName = line.substr(1, line.length() - 2);
593 STORM_LOG_THROW(moduleName != "" && (moduleName == "global" || (this->modules.find(moduleName) != this->modules.end())),
594 storm::exceptions::OptionParserException,
595 "Module header in configuration file '" << filename << " in line " << std::to_string(lineNumber) << " refers to unknown module '"
596 << moduleName << ".");
597
598 // If the module name is "global", we unset the currently active module and treat all options to follow as unprefixed.
599 if (moduleName == "global") {
600 globalScope = true;
601 } else {
602 activeModule = moduleName;
603 globalScope = false;
604 }
605 } else {
606 // In this case, we expect the line to be of the shape o or o=a b c, where o is an option and a, b
607 // and c are the values that are supposed to be assigned to the arguments of the option.
608 std::size_t assignmentSignIndex = line.find("=");
609 bool containsAssignment = false;
610 if (assignmentSignIndex != std::string::npos) {
611 containsAssignment = true;
612 }
613
614 std::string optionName;
615 if (containsAssignment) {
616 optionName = line.substr(0, assignmentSignIndex);
617 } else {
618 optionName = line;
619 }
620
621 if (globalScope) {
622 STORM_LOG_THROW(this->longNameToOptions.find(optionName) != this->longNameToOptions.end(), storm::exceptions::OptionParserException,
623 "Option assignment in configuration file '" << filename << " in line " << lineNumber << " refers to unknown option '"
624 << optionName << "'.");
625 } else {
626 STORM_LOG_THROW(this->longNameToOptions.find(activeModule + ":" + optionName) != this->longNameToOptions.end(),
627 storm::exceptions::OptionParserException,
628 "Option assignment in configuration file '" << filename << " in line " << lineNumber << " refers to unknown option '"
629 << activeModule << ":" << optionName << "'.");
630 }
631
632 std::string fullOptionName = (!globalScope ? activeModule + ":" : "") + optionName;
633 STORM_LOG_WARN_COND(result.find(fullOptionName) == result.end(), "Option '" << fullOptionName << "' is set in line " << lineNumber
634 << " of configuration file " << filename
635 << ", but has been set before.");
636
637 // If the current line is an assignment, split the right-hand side of the assignment into parts
638 // enclosed by quotation marks.
639 if (containsAssignment) {
640 std::string assignedValues = line.substr(assignmentSignIndex + 1);
641 std::vector<std::string> argumentCache;
642
643 // As horrible as it may look, this regular expression matches either a quoted string (possibly
644 // containing escaped quotes) or a simple word (without whitespaces and quotes).
645 std::regex argumentRegex("\"(([^\\\\\"]|((\\\\\\\\)*\\\\\")|\\\\[^\"])*)\"|(([^ \\\\\"]|((\\\\\\\\)*\\\\\")|\\\\[^\"])+)");
646 boost::algorithm::trim_left(assignedValues);
647
648 while (!assignedValues.empty()) {
649 std::smatch match;
650 bool hasMatch = std::regex_search(assignedValues, match, argumentRegex);
651
652 // If the input could not be matched, we have a parsing error.
654 hasMatch, storm::exceptions::OptionParserException,
655 "Parsing error in configuration file '" << filename << "' in line " << lineNumber << ". Unexpected input '" << assignedValues << "'.");
656
657 // Extract the matched argument and cut off the quotation marks if necessary.
658 std::string matchedArgument = std::string(match[0].first, match[0].second);
659 if (matchedArgument.at(0) == '"') {
660 matchedArgument = matchedArgument.substr(1, matchedArgument.length() - 2);
661 }
662 argumentCache.push_back(matchedArgument);
663
664 assignedValues = assignedValues.substr(match.length());
665 boost::algorithm::trim_left(assignedValues);
666 }
667
668 // After successfully parsing the argument values, we store them in the result map.
669 result.emplace(fullOptionName, argumentCache);
670 } else {
671 // In this case, we can just insert the option to indicate it should be set (without arguments).
672 result.emplace(fullOptionName, std::vector<std::string>());
673 }
674 }
675 }
676
678 return result;
679}
680
684
688
692
696
697void initializeAll(std::string const& name, std::string const& executableName) {
698 storm::settings::mutableManager().setName(name, executableName);
699
700 // Register all known settings modules.
730}
731
732} // namespace settings
733} // namespace storm
Provides the central API for the registration of command line options and parsing the options from th...
void setFromCommandLine(int const argc, char const *const argv[])
This function parses the given command line arguments and sets all registered options accordingly.
std::string getHelpForModule(std::string const &moduleName, uint_fast64_t maxLength=30, bool includeAdvanced=true) const
This function prints a help message for the specified module to the standard output.
void setFromExplodedString(std::vector< std::string > const &commandLineArguments)
This function parses the given command line arguments (represented by several strings) and sets all r...
void setFromString(std::string const &commandLineString)
This function parses the given command line arguments (represented by one big string) and sets all re...
void setFromConfigurationFile(std::string const &configFilename)
This function parses the given file and sets all registered options accordingly.
void addModule(std::unique_ptr< modules::ModuleSettings > &&moduleSettings, bool doRegister=true)
Adds a new module with the given name.
void printHelp(std::string const &filter="frequent") const
This function prints a help message to the standard output.
void handleUnknownOption(std::string const &optionName, bool isShort) const
Throws an exception with a nice error message indicating similar valid option names.
void setName(std::string const &name, std::string const &executableName)
Sets the name of the tool.
SettingsManager(SettingsManager const &)=delete
modules::ModuleSettings const & getModule(std::string const &moduleName) const
Retrieves the settings of the module with the given name.
bool hasModule(std::string const &moduleName, bool checkHidden=false) const
Checks whether the module with the given name exists.
static SettingsManager & manager()
Retrieves the only existing instance of a settings manager.
This class represents the settings for the abstraction procedures.
This is the base class of the settings for a particular module.
bool add(std::string const &string)
Adds the given string to the set of similar strings (if it is similar).
Definition string.cpp:21
std::string toDidYouMeanString() const
Returns a "Did you mean abc?" string.
Definition string.cpp:37
std::vector< std::string > toList() const
Gets a list of all added strings that are similar to the reference string.
Definition string.cpp:29
#define STORM_LOG_WARN(message)
Definition logging.h:28
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:36
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
std::basic_istream< CharT, Traits > & getline(std::basic_istream< CharT, Traits > &input, std::basic_string< CharT, Traits, Allocator > &str)
Overloaded getline function which handles different types of newline ( and \r).
Definition file.h:80
void closeFile(std::ofstream &stream)
Close the given file after writing.
Definition file.h:47
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
Definition file.h:18
storm::settings::modules::BuildSettings & mutableBuildSettings()
Retrieves the build settings in a mutable form.
storm::settings::modules::AbstractionSettings & mutableAbstractionSettings()
Retrieves the abstraction settings in a mutable form.
bool hasModule()
Returns true if the given module is registered.
SettingsType const & getModule()
Get module.
void addModule(bool doRegister=true)
Add new module to use for the settings.
void initializeAll(std::string const &name, std::string const &executableName)
Initialize the settings manager with all available modules.
SettingsManager const & manager()
Retrieves the settings manager.
SettingsManager & mutableManager()
Retrieves the settings manager.