diff options
-rw-r--r-- | qmake/doc/src/qmake-manual.qdoc | 36 | ||||
-rw-r--r-- | qmake/library/qmakebuiltins.cpp | 108 | ||||
-rw-r--r-- | qmake/library/qmakeevaluator.h | 3 | ||||
-rw-r--r-- | tests/auto/tools/qmakelib/evaltest.cpp | 43 |
4 files changed, 153 insertions, 37 deletions
diff --git a/qmake/doc/src/qmake-manual.qdoc b/qmake/doc/src/qmake-manual.qdoc index 70210b7342..5828d515e5 100644 --- a/qmake/doc/src/qmake-manual.qdoc +++ b/qmake/doc/src/qmake-manual.qdoc @@ -2934,6 +2934,7 @@ See also \l{upper(arg1 [, arg2 ..., argn])}{upper()}. + \target member() \section2 member(variablename [, start [, end]]) Returns the slice of the list value of \c variablename with the @@ -2960,6 +2961,9 @@ that an empty list will be returned only when an index is invalid (which is implied by the input variable being empty). + See also \l{str_member()}. + + \target num_add() \section2 num_add(arg1 [, arg2 ..., argn]) Takes an arbitrary number of numeric arguments and adds them up, @@ -3079,6 +3083,38 @@ \snippet code/doc_src_qmake-manual.pro 168 + \target str_member() + \section2 str_member(arg [, start [, end]]) + + This function is identical to \l{member()}, except that it operates + on a string value instead of a list variable, and consequently the + indices refer to character positions. + + This function can be used to implement many common string slicing + operations: + + \code + # $$left(VAR, len) + left = $$str_member(VAR, 0, $$num_add($$len, -1)) + + # $$right(VAR, len) + right = $$str_member(VAR, -$$num, -1) + + # $$mid(VAR, off, len) + mid = $$str_member(VAR, $$off, $$num_add($$off, $$len, -1)) + + # $$mid(VAR, off) + mid = $$str_member(VAR, $$off, -1) + + # $$reverse(VAR) + reverse = $$str_member(VAR, -1, 0) + \endcode + + \note In these implementations, a zero \c len argument needs to be + handled separately. + + See also \l{member()}, \l{num_add()}. + \target str_size() \section2 str_size(arg) diff --git a/qmake/library/qmakebuiltins.cpp b/qmake/library/qmakebuiltins.cpp index d21bae3060..6c8a079faf 100644 --- a/qmake/library/qmakebuiltins.cpp +++ b/qmake/library/qmakebuiltins.cpp @@ -80,7 +80,7 @@ QT_BEGIN_NAMESPACE #define fL1S(s) QString::fromLatin1(s) enum ExpandFunc { - E_INVALID = 0, E_MEMBER, E_FIRST, E_TAKE_FIRST, E_LAST, E_TAKE_LAST, + E_INVALID = 0, E_MEMBER, E_STR_MEMBER, E_FIRST, E_TAKE_FIRST, E_LAST, E_TAKE_LAST, E_SIZE, E_STR_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST, E_SPRINTF, E_FORMAT_NUMBER, E_NUM_ADD, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION, E_FIND, E_SYSTEM, E_UNIQUE, E_REVERSE, E_QUOTE, E_ESCAPE_EXPAND, @@ -105,6 +105,7 @@ void QMakeEvaluator::initFunctionStatics() const ExpandFunc func; } expandInits[] = { { "member", E_MEMBER }, + { "str_member", E_STR_MEMBER }, { "first", E_FIRST }, { "take_first", E_TAKE_FIRST }, { "last", E_LAST }, @@ -202,6 +203,49 @@ static bool isTrue(const ProString &str) return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt(); } +bool +QMakeEvaluator::getMemberArgs(const ProKey &func, int srclen, const ProStringList &args, + int *start, int *end) +{ + *start = 0, *end = 0; + if (args.count() >= 2) { + bool ok = true; + const ProString &start_str = args.at(1); + *start = start_str.toInt(&ok); + if (!ok) { + if (args.count() == 2) { + int dotdot = start_str.indexOf(statics.strDotDot); + if (dotdot != -1) { + *start = start_str.left(dotdot).toInt(&ok); + if (ok) + *end = start_str.mid(dotdot+2).toInt(&ok); + } + } + if (!ok) { + evalError(fL1S("%1() argument 2 (start) '%2' invalid.") + .arg(func.toQString(m_tmp1), start_str.toQString(m_tmp2))); + return false; + } + } else { + *end = *start; + if (args.count() == 3) + *end = args.at(2).toInt(&ok); + if (!ok) { + evalError(fL1S("%1() argument 3 (end) '%2' invalid.") + .arg(func.toQString(m_tmp1), args.at(2).toQString(m_tmp2))); + return false; + } + } + } + if (*start < 0) + *start += srclen; + if (*end < 0) + *end += srclen; + if (*start < 0 || *start >= srclen || *end < 0 || *end >= srclen) + return false; + return true; +} + #if defined(Q_OS_WIN) && defined(PROEVALUATOR_FULL) static QString windowsErrorCode() { @@ -660,47 +704,37 @@ ProStringList QMakeEvaluator::evaluateBuiltinExpand( if (args.count() < 1 || args.count() > 3) { evalError(fL1S("member(var, start, end) requires one to three arguments.")); } else { - bool ok = true; - const ProStringList &var = values(map(args.at(0))); - int start = 0, end = 0; - if (args.count() >= 2) { - const ProString &start_str = args.at(1); - start = start_str.toInt(&ok); - if (!ok) { - if (args.count() == 2) { - int dotdot = start_str.indexOf(statics.strDotDot); - if (dotdot != -1) { - start = start_str.left(dotdot).toInt(&ok); - if (ok) - end = start_str.mid(dotdot+2).toInt(&ok); - } - } - if (!ok) - evalError(fL1S("member() argument 2 (start) '%2' invalid.") - .arg(start_str.toQString(m_tmp1))); + const ProStringList &src = values(map(args.at(0))); + int start, end; + if (getMemberArgs(func, src.size(), args, &start, &end)) { + ret.reserve(qAbs(end - start) + 1); + if (start < end) { + for (int i = start; i <= end && src.size() >= i; i++) + ret += src.at(i); } else { - end = start; - if (args.count() == 3) - end = args.at(2).toInt(&ok); - if (!ok) - evalError(fL1S("member() argument 3 (end) '%2' invalid.") - .arg(args.at(2).toQString(m_tmp1))); + for (int i = start; i >= end && src.size() >= i && i >= 0; i--) + ret += src.at(i); } } - if (ok) { - if (start < 0) - start += var.count(); - if (end < 0) - end += var.count(); - if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) { - //nothing - } else if (start < end) { - for (int i = start; i <= end && var.count() >= i; i++) - ret.append(var[i]); + } + break; + case E_STR_MEMBER: + if (args.count() < 1 || args.count() > 3) { + evalError(fL1S("str_member(str, start, end) requires one to three arguments.")); + } else { + const ProString &src = args.at(0); + int start, end; + if (getMemberArgs(func, src.size(), args, &start, &end)) { + QString res; + res.reserve(qAbs(end - start) + 1); + if (start < end) { + for (int i = start; i <= end && src.size() >= i; i++) + res += src.at(i); } else { - for (int i = start; i >= end && var.count() >= i && i >= 0; i--) - ret += var[i]; + for (int i = start; i >= end && src.size() >= i && i >= 0; i--) + res += src.at(i); } + ret += ProString(res); } } break; diff --git a/qmake/library/qmakeevaluator.h b/qmake/library/qmakeevaluator.h index 0437831069..7b911e7911 100644 --- a/qmake/library/qmakeevaluator.h +++ b/qmake/library/qmakeevaluator.h @@ -232,6 +232,9 @@ public: QHash<ProKey, QSet<ProKey> > &dependencies, ProValueMap &dependees, QMultiMap<int, ProString> &rootSet) const; + bool getMemberArgs(const ProKey &name, int srclen, const ProStringList &args, + int *start, int *end); + VisitReturn writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode, bool exe, const QString &contents); #ifndef QT_BOOTSTRAPPED diff --git a/tests/auto/tools/qmakelib/evaltest.cpp b/tests/auto/tools/qmakelib/evaltest.cpp index f12b11261a..03fbf96a9f 100644 --- a/tests/auto/tools/qmakelib/evaltest.cpp +++ b/tests/auto/tools/qmakelib/evaltest.cpp @@ -718,6 +718,49 @@ void tst_qmakelib::addReplaceFunctions(const QString &qindir) << "##:2: member() argument 2 (start) '4..foo' invalid." << true; + // The argument processing is shared with $$member(), so some tests are skipped. + QTest::newRow("$$str_member(): empty") + << "VAR = $$str_member()" + << "VAR =" + << "" + << true; + + QTest::newRow("$$str_member(): too short") + << "VAR = $$str_member(string_value, 7, 12)" + << "VAR =" // this is actually kinda stupid + << "" + << true; + + QTest::newRow("$$str_member(): ok") + << "VAR = $$str_member(string_value, 7, 11)" + << "VAR = value" + << "" + << true; + + QTest::newRow("$$str_member(): ok (default start)") + << "VAR = $$str_member(string_value)" + << "VAR = s" + << "" + << true; + + QTest::newRow("$$str_member(): ok (default end)") + << "VAR = $$str_member(string_value, 7)" + << "VAR = v" + << "" + << true; + + QTest::newRow("$$str_member(): negative") + << "VAR = $$str_member(string_value, -5, -3)" + << "VAR = val" + << "" + << true; + + QTest::newRow("$$str_member(): inverse") + << "VAR = $$str_member(string_value, -2, 1)" + << "VAR = ulav_gnirt" + << "" + << true; + QTest::newRow("$$first(): empty") << "IN = \nVAR = $$first(IN)" << "VAR =" |