Improve readability of ArgumentReader::read and use Phrases

This commit is contained in:
Martchus 2017-10-19 00:48:05 +02:00
parent e459dca98d
commit 46f652ad00
2 changed files with 160 additions and 151 deletions

View File

@ -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)
} }

View File

@ -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);
} }
} }