Browse Source

Improve readability of ArgumentReader::read and use Phrases

experiment/srcref_basic_cfg
Martchus 5 years ago
parent
commit
46f652ad00
  1. 288
      application/argumentparser.cpp
  2. 13
      tests/argumentparsertests.cpp

288
application/argumentparser.cpp

@ -18,6 +18,7 @@
using namespace std;
using namespace std::placeholders;
using namespace ConversionUtilities;
using namespace EscapeCodes;
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
while (argv != end) {
if (values && lastArgInLevel->requiredValueCount() != static_cast<size_t>(-1) && values->size() < lastArgInLevel->requiredValueCount()) {
// there are still values to read
// check whether 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);
++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 {
// determine how denotation must be processed
bool abbreviationFound = false;
if (argDenotation) {
// continue reading childs for abbreviation denotation already detected
abbreviationFound = false;
argDenotationType = Abbreviation;
} 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);
// 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);
}
// try to find matching Argument instance
Argument *matchingArg = nullptr;
size_t argDenotationLength;
if (argDenotationType != Value) {
const char *const equationPos = strchr(argDenotation, '=');
for (argDenotationLength = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation);
argDenotationLength; matchingArg = nullptr) {
// search for arguments by abbreviation or name depending on the previously determined denotation type
if (argDenotationType == Abbreviation) {
for (Argument *arg : args) {
if (arg->abbreviation() && arg->abbreviation() == *argDenotation) {
matchingArg = arg;
abbreviationFound = true;
break;
}
// try to find matching Argument instance
Argument *matchingArg = nullptr;
size_t argDenotationLength;
if (argDenotationType != Value) {
const char *const equationPos = strchr(argDenotation, '=');
for (argDenotationLength = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation);
argDenotationLength; matchingArg = nullptr) {
// search for arguments by abbreviation or name depending on the previously determined denotation type
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;
}
}
} 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);
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);
}
// 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 {
// 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;
// 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;
}
};
}
}
// 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;
if (parentArgument == pathEnd) {
break;
}
};
}
// 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;
}
}
// 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;
}
// 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;
}
}
// 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
}
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;
// 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;
}
// 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 << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << endl;
FALLTHROUGH;
case UnknownArgumentBehavior::Ignore:
// ignore unknown denotation
++index, ++argv, argDenotation = nullptr;
break;
case UnknownArgumentBehavior::Fail:
throw Failure(argsToString("The specified argument \"", *argv, "\" is unknown."));
}
}
} // if(!matchingArg)
} // no values to read
// 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)
}

13
tests/argumentparsertests.cpp

@ -9,6 +9,7 @@
#include "../application/failure.h"
#include "../application/fakeqtconfigarguments.h"
#include "../io/ansiescapecodes.h"
#include "../io/path.h"
#include "resources/config.h"
@ -187,6 +188,7 @@ void ArgumentParserTests::testParsing()
stringstream buffer;
streambuf *regularCerrBuffer = cerr.rdbuf(buffer.rdbuf());
parser.resetArgs();
EscapeCodes::enabled = false;
try {
parser.parseArgs(6, argv3);
} catch (...) {
@ -194,11 +196,11 @@ void ArgumentParserTests::testParsing()
throw;
}
cerr.rdbuf(regularCerrBuffer);
CPPUNIT_ASSERT_EQUAL("The specified argument \"album\" is unknown and will be ignored.\n"s
"The specified argument \"title\" is unknown and will be ignored.\n"s
"The specified argument \"diskpos\" is unknown and will be ignored.\n"s
"The specified argument \"--files\" is unknown and will be ignored.\n"s
"The specified argument \"somefile\" is unknown and will be ignored.\n"s,
CPPUNIT_ASSERT_EQUAL("Warning: The specified argument \"album\" is unknown and will be ignored.\n"s
"Warning: The specified argument \"title\" is unknown and will be ignored.\n"s
"Warning: The specified argument \"diskpos\" is unknown and will be ignored.\n"s
"Warning: The specified argument \"--files\" is unknown and will be ignored.\n"s
"Warning: The specified argument \"somefile\" is unknown and will be ignored.\n"s,
buffer.str());
// none of the arguments should be present now
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
@ -666,6 +668,7 @@ void ArgumentParserTests::testHelp()
" default environment variable: FILES\n"
"\n"
"Project website: " APP_URL "\n");
EscapeCodes::enabled = true;
parser.parseArgs(2, argv);
}
}

Loading…
Cancel
Save