Improve readability of ArgumentReader::read and use Phrases
This commit is contained in:
parent
e459dca98d
commit
46f652ad00
|
@ -18,6 +18,7 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
using namespace ConversionUtilities;
|
using namespace ConversionUtilities;
|
||||||
|
using namespace EscapeCodes;
|
||||||
using namespace IoUtilities;
|
using namespace IoUtilities;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -97,172 +98,177 @@ void ArgumentReader::read(ArgumentVector &args)
|
||||||
|
|
||||||
// iterate through all argument denotations; loop might exit earlier when an denotation is unknown
|
// iterate through all argument denotations; loop might exit earlier when an denotation is unknown
|
||||||
while (argv != end) {
|
while (argv != end) {
|
||||||
if (values && lastArgInLevel->requiredValueCount() != static_cast<size_t>(-1) && values->size() < lastArgInLevel->requiredValueCount()) {
|
// check whether there are still values to read
|
||||||
// there are still values to read
|
if (values && lastArgInLevel->requiredValueCount() != Argument::varValueCount && values->size() < lastArgInLevel->requiredValueCount()) {
|
||||||
|
// read arg as value and continue with next arg
|
||||||
values->emplace_back(argDenotation ? argDenotation : *argv);
|
values->emplace_back(argDenotation ? argDenotation : *argv);
|
||||||
++index, ++argv, argDenotation = nullptr;
|
++index, ++argv, argDenotation = nullptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine how denotation must be processed
|
||||||
|
bool abbreviationFound = false;
|
||||||
|
if (argDenotation) {
|
||||||
|
// continue reading childs for abbreviation denotation already detected
|
||||||
|
abbreviationFound = false;
|
||||||
|
argDenotationType = Abbreviation;
|
||||||
} else {
|
} else {
|
||||||
// determine how denotation must be processed
|
// determine denotation type
|
||||||
bool abbreviationFound = false;
|
argDenotation = *argv;
|
||||||
if (argDenotation) {
|
if (!*argDenotation && (!lastArgInLevel || values->size() >= lastArgInLevel->requiredValueCount())) {
|
||||||
// continue reading childs for abbreviation denotation already detected
|
// skip empty arguments
|
||||||
abbreviationFound = false;
|
++index, ++argv, argDenotation = nullptr;
|
||||||
argDenotationType = Abbreviation;
|
continue;
|
||||||
} else {
|
|
||||||
// determine denotation type
|
|
||||||
argDenotation = *argv;
|
|
||||||
if (!*argDenotation && (!lastArgInLevel || values->size() >= lastArgInLevel->requiredValueCount())) {
|
|
||||||
// skip empty arguments
|
|
||||||
++index, ++argv, argDenotation = nullptr;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
abbreviationFound = false;
|
|
||||||
argDenotationType = Value;
|
|
||||||
*argDenotation == '-' && (++argDenotation, ++argDenotationType) && *argDenotation == '-' && (++argDenotation, ++argDenotationType);
|
|
||||||
}
|
}
|
||||||
|
abbreviationFound = false;
|
||||||
|
argDenotationType = Value;
|
||||||
|
*argDenotation == '-' && (++argDenotation, ++argDenotationType) && *argDenotation == '-' && (++argDenotation, ++argDenotationType);
|
||||||
|
}
|
||||||
|
|
||||||
// try to find matching Argument instance
|
// try to find matching Argument instance
|
||||||
Argument *matchingArg = nullptr;
|
Argument *matchingArg = nullptr;
|
||||||
size_t argDenotationLength;
|
size_t argDenotationLength;
|
||||||
if (argDenotationType != Value) {
|
if (argDenotationType != Value) {
|
||||||
const char *const equationPos = strchr(argDenotation, '=');
|
const char *const equationPos = strchr(argDenotation, '=');
|
||||||
for (argDenotationLength = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation);
|
for (argDenotationLength = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation);
|
||||||
argDenotationLength; matchingArg = nullptr) {
|
argDenotationLength; matchingArg = nullptr) {
|
||||||
// search for arguments by abbreviation or name depending on the previously determined denotation type
|
// search for arguments by abbreviation or name depending on the previously determined denotation type
|
||||||
if (argDenotationType == Abbreviation) {
|
if (argDenotationType == Abbreviation) {
|
||||||
for (Argument *arg : args) {
|
|
||||||
if (arg->abbreviation() && arg->abbreviation() == *argDenotation) {
|
|
||||||
matchingArg = arg;
|
|
||||||
abbreviationFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (Argument *arg : args) {
|
|
||||||
if (arg->name() && !strncmp(arg->name(), argDenotation, argDenotationLength)
|
|
||||||
&& *(arg->name() + argDenotationLength) == '\0') {
|
|
||||||
matchingArg = arg;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchingArg) {
|
|
||||||
// an argument matched the specified denotation so add an occurrence
|
|
||||||
matchingArg->m_occurrences.emplace_back(index, parentPath, parentArg);
|
|
||||||
|
|
||||||
// prepare reading parameter values
|
|
||||||
values = &matchingArg->m_occurrences.back().values;
|
|
||||||
if (equationPos) {
|
|
||||||
values->push_back(equationPos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// read sub arguments
|
|
||||||
++index, ++parser.m_actualArgc, lastArg = lastArgInLevel = matchingArg, lastArgDenotation = argv;
|
|
||||||
if (argDenotationType != Abbreviation || (++argDenotation != equationPos)) {
|
|
||||||
if (argDenotationType != Abbreviation || !*argDenotation) {
|
|
||||||
// no further abbreviations follow -> read sub args for next argv
|
|
||||||
++argv, argDenotation = nullptr;
|
|
||||||
read(lastArg->m_subArgs);
|
|
||||||
argDenotation = nullptr;
|
|
||||||
} else {
|
|
||||||
// further abbreviations follow -> don't increment argv, keep processing outstanding chars of argDenotation
|
|
||||||
read(lastArg->m_subArgs);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} // else: another abbreviated argument follows (and it is not present in the sub args)
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!matchingArg) {
|
|
||||||
// unknown argument might be a sibling of the parent element
|
|
||||||
if (argDenotationType != Value) {
|
|
||||||
for (auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
|
|
||||||
for (Argument *sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() : parser.m_mainArgs)) {
|
|
||||||
if (sibling->occurrences() < sibling->maxOccurrences()) {
|
|
||||||
if ((argDenotationType == Abbreviation && (sibling->abbreviation() && sibling->abbreviation() == *argDenotation))
|
|
||||||
|| (sibling->name() && !strncmp(sibling->name(), argDenotation, argDenotationLength))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (parentArgument == pathEnd) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// unknown argument might just be a parameter value of the last argument
|
|
||||||
if (lastArgInLevel && values->size() < lastArgInLevel->requiredValueCount()) {
|
|
||||||
values->emplace_back(abbreviationFound ? argDenotation : *argv);
|
|
||||||
++index, ++argv, argDenotation = nullptr;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// first value might denote "operation"
|
|
||||||
for (Argument *arg : args) {
|
|
||||||
if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
|
|
||||||
(matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
|
|
||||||
lastArgDenotation = argv;
|
|
||||||
++index, ++argv;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use the first default argument which is not already present if there is still no match
|
|
||||||
if (!matchingArg && (!completionMode || (argv + 1 != end))) {
|
|
||||||
const bool uncombinableMainArgPresent = parentArg ? false : parser.isUncombinableMainArgPresent();
|
|
||||||
for (Argument *arg : args) {
|
for (Argument *arg : args) {
|
||||||
if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
|
if (arg->abbreviation() && arg->abbreviation() == *argDenotation) {
|
||||||
&& (!uncombinableMainArgPresent || !arg->isMainArgument())) {
|
matchingArg = arg;
|
||||||
(matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
|
abbreviationFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (Argument *arg : args) {
|
||||||
|
if (arg->name() && !strncmp(arg->name(), argDenotation, argDenotationLength)
|
||||||
|
&& *(arg->name() + argDenotationLength) == '\0') {
|
||||||
|
matchingArg = arg;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchingArg) {
|
if (matchingArg) {
|
||||||
// an argument matched the specified denotation
|
// an argument matched the specified denotation so add an occurrence
|
||||||
if (lastArgInLevel == matchingArg) {
|
matchingArg->m_occurrences.emplace_back(index, parentPath, parentArg);
|
||||||
break; // break required? -> TODO: add test for this condition
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare reading parameter values
|
// prepare reading parameter values
|
||||||
values = &matchingArg->m_occurrences.back().values;
|
values = &matchingArg->m_occurrences.back().values;
|
||||||
|
if (equationPos) {
|
||||||
|
values->push_back(equationPos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
// read sub arguments
|
// read sub arguments
|
||||||
++parser.m_actualArgc, lastArg = lastArgInLevel = matchingArg, argDenotation = nullptr;
|
++index, ++parser.m_actualArgc, lastArg = lastArgInLevel = matchingArg, lastArgDenotation = argv;
|
||||||
read(lastArg->m_subArgs);
|
if (argDenotationType != Abbreviation || (++argDenotation != equationPos)) {
|
||||||
argDenotation = nullptr;
|
if (argDenotationType != Abbreviation || !*argDenotation) {
|
||||||
continue;
|
// no further abbreviations follow -> read sub args for next argv
|
||||||
}
|
++argv, argDenotation = nullptr;
|
||||||
|
read(lastArg->m_subArgs);
|
||||||
// argument denotation is unknown -> handle error
|
argDenotation = nullptr;
|
||||||
if (parentArg) {
|
} else {
|
||||||
// continue with parent level
|
// further abbreviations follow -> don't increment argv, keep processing outstanding chars of argDenotation
|
||||||
return;
|
read(lastArg->m_subArgs);
|
||||||
}
|
}
|
||||||
if (completionMode) {
|
|
||||||
// ignore unknown denotation
|
|
||||||
++index, ++argv, argDenotation = nullptr;
|
|
||||||
} else {
|
|
||||||
switch (parser.m_unknownArgBehavior) {
|
|
||||||
case UnknownArgumentBehavior::Warn:
|
|
||||||
cerr << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << endl;
|
|
||||||
FALLTHROUGH;
|
|
||||||
case UnknownArgumentBehavior::Ignore:
|
|
||||||
// ignore unknown denotation
|
|
||||||
++index, ++argv, argDenotation = nullptr;
|
|
||||||
break;
|
break;
|
||||||
case UnknownArgumentBehavior::Fail:
|
} // else: another abbreviated argument follows (and it is not present in the sub args)
|
||||||
throw Failure(argsToString("The specified argument \"", *argv, "\" is unknown."));
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// continue with next arg if we've got a match already
|
||||||
|
if (matchingArg) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unknown argument might be a sibling of the parent element
|
||||||
|
if (argDenotationType != Value) {
|
||||||
|
for (auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
|
||||||
|
for (Argument *sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() : parser.m_mainArgs)) {
|
||||||
|
if (sibling->occurrences() < sibling->maxOccurrences()) {
|
||||||
|
if ((argDenotationType == Abbreviation && (sibling->abbreviation() && sibling->abbreviation() == *argDenotation))
|
||||||
|
|| (sibling->name() && !strncmp(sibling->name(), argDenotation, argDenotationLength))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // if(!matchingArg)
|
if (parentArgument == pathEnd) {
|
||||||
} // no values to read
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// unknown argument might just be a parameter value of the last argument
|
||||||
|
if (lastArgInLevel && values->size() < lastArgInLevel->requiredValueCount()) {
|
||||||
|
values->emplace_back(abbreviationFound ? argDenotation : *argv);
|
||||||
|
++index, ++argv, argDenotation = nullptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first value might denote "operation"
|
||||||
|
for (Argument *arg : args) {
|
||||||
|
if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
|
||||||
|
(matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
|
||||||
|
lastArgDenotation = argv;
|
||||||
|
++index, ++argv;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use the first default argument which is not already present if there is still no match
|
||||||
|
if (!matchingArg && (!completionMode || (argv + 1 != end))) {
|
||||||
|
const bool uncombinableMainArgPresent = parentArg ? false : parser.isUncombinableMainArgPresent();
|
||||||
|
for (Argument *arg : args) {
|
||||||
|
if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
|
||||||
|
&& (!uncombinableMainArgPresent || !arg->isMainArgument())) {
|
||||||
|
(matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingArg) {
|
||||||
|
// an argument matched the specified denotation
|
||||||
|
if (lastArgInLevel == matchingArg) {
|
||||||
|
break; // break required? -> TODO: add test for this condition
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare reading parameter values
|
||||||
|
values = &matchingArg->m_occurrences.back().values;
|
||||||
|
|
||||||
|
// read sub arguments
|
||||||
|
++parser.m_actualArgc, lastArg = lastArgInLevel = matchingArg, argDenotation = nullptr;
|
||||||
|
read(lastArg->m_subArgs);
|
||||||
|
argDenotation = nullptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// argument denotation is unknown -> handle error
|
||||||
|
if (parentArg) {
|
||||||
|
// continue with parent level
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (completionMode) {
|
||||||
|
// ignore unknown denotation
|
||||||
|
++index, ++argv, argDenotation = nullptr;
|
||||||
|
} else {
|
||||||
|
switch (parser.m_unknownArgBehavior) {
|
||||||
|
case UnknownArgumentBehavior::Warn:
|
||||||
|
cerr << Phrases::Warning << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << Phrases::EndFlush;
|
||||||
|
FALLTHROUGH;
|
||||||
|
case UnknownArgumentBehavior::Ignore:
|
||||||
|
// ignore unknown denotation
|
||||||
|
++index, ++argv, argDenotation = nullptr;
|
||||||
|
break;
|
||||||
|
case UnknownArgumentBehavior::Fail:
|
||||||
|
throw Failure(argsToString("The specified argument \"", *argv, "\" is unknown."));
|
||||||
|
}
|
||||||
|
}
|
||||||
} // while(argv != end)
|
} // while(argv != end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "../application/failure.h"
|
#include "../application/failure.h"
|
||||||
#include "../application/fakeqtconfigarguments.h"
|
#include "../application/fakeqtconfigarguments.h"
|
||||||
|
|
||||||
|
#include "../io/ansiescapecodes.h"
|
||||||
#include "../io/path.h"
|
#include "../io/path.h"
|
||||||
|
|
||||||
#include "resources/config.h"
|
#include "resources/config.h"
|
||||||
|
@ -187,6 +188,7 @@ void ArgumentParserTests::testParsing()
|
||||||
stringstream buffer;
|
stringstream buffer;
|
||||||
streambuf *regularCerrBuffer = cerr.rdbuf(buffer.rdbuf());
|
streambuf *regularCerrBuffer = cerr.rdbuf(buffer.rdbuf());
|
||||||
parser.resetArgs();
|
parser.resetArgs();
|
||||||
|
EscapeCodes::enabled = false;
|
||||||
try {
|
try {
|
||||||
parser.parseArgs(6, argv3);
|
parser.parseArgs(6, argv3);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
@ -194,11 +196,11 @@ void ArgumentParserTests::testParsing()
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
cerr.rdbuf(regularCerrBuffer);
|
cerr.rdbuf(regularCerrBuffer);
|
||||||
CPPUNIT_ASSERT_EQUAL("The specified argument \"album\" is unknown and will be ignored.\n"s
|
CPPUNIT_ASSERT_EQUAL("Warning: The specified argument \"album\" is unknown and will be ignored.\n"s
|
||||||
"The specified argument \"title\" is unknown and will be ignored.\n"s
|
"Warning: The specified argument \"title\" is unknown and will be ignored.\n"s
|
||||||
"The specified argument \"diskpos\" is unknown and will be ignored.\n"s
|
"Warning: The specified argument \"diskpos\" is unknown and will be ignored.\n"s
|
||||||
"The specified argument \"--files\" is unknown and will be ignored.\n"s
|
"Warning: The specified argument \"--files\" is unknown and will be ignored.\n"s
|
||||||
"The specified argument \"somefile\" is unknown and will be ignored.\n"s,
|
"Warning: The specified argument \"somefile\" is unknown and will be ignored.\n"s,
|
||||||
buffer.str());
|
buffer.str());
|
||||||
// none of the arguments should be present now
|
// none of the arguments should be present now
|
||||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||||
|
@ -666,6 +668,7 @@ void ArgumentParserTests::testHelp()
|
||||||
" default environment variable: FILES\n"
|
" default environment variable: FILES\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Project website: " APP_URL "\n");
|
"Project website: " APP_URL "\n");
|
||||||
|
EscapeCodes::enabled = true;
|
||||||
parser.parseArgs(2, argv);
|
parser.parseArgs(2, argv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue