diff --git a/src/shared/proparser/abstractproitemvisitor.h b/src/shared/proparser/abstractproitemvisitor.h index 9e8e0582b28c6b04da178cf8737bddf91e9cc32b..85bfe42ea615d14f2e534da22f44d3e97e3091eb 100644 --- a/src/shared/proparser/abstractproitemvisitor.h +++ b/src/shared/proparser/abstractproitemvisitor.h @@ -41,6 +41,9 @@ struct AbstractProItemVisitor virtual ProItem::ProItemReturn visitBeginProBlock(ProBlock *block) = 0; virtual void visitEndProBlock(ProBlock *block) = 0; + virtual ProItem::ProItemReturn visitProLoopIteration() = 0; + virtual void visitProLoopCleanup() = 0; + virtual void visitBeginProVariable(ProVariable *variable) = 0; virtual void visitEndProVariable(ProVariable *variable) = 0; diff --git a/src/shared/proparser/profileevaluator.cpp b/src/shared/proparser/profileevaluator.cpp index 5887cc3f252786074896fc0454ddf88946ea8278..58d737221e5811c1b03a6cd9afee8683ae329c2c 100644 --- a/src/shared/proparser/profileevaluator.cpp +++ b/src/shared/proparser/profileevaluator.cpp @@ -159,6 +159,8 @@ public: // implementation of AbstractProItemVisitor ProItem::ProItemReturn visitBeginProBlock(ProBlock *block); void visitEndProBlock(ProBlock *block); + ProItem::ProItemReturn visitProLoopIteration(); + void visitProLoopCleanup(); void visitBeginProVariable(ProVariable *variable); void visitEndProVariable(ProVariable *variable); ProItem::ProItemReturn visitBeginProFile(ProFile *value); @@ -180,6 +182,7 @@ public: void expandPatternHelper(const QString &relName, const QString &absName, QStringList &sources_out); QStringList expandVariableReferences(const QString &value); + void doVariableReplace(QString *str); QStringList evaluateExpandFunction(const QString &function, const QString &arguments); QString format(const char *format) const; @@ -211,6 +214,14 @@ public: QString m_origfile; QString m_oldPath; // To restore the current path to the path QStack<ProFile*> m_profileStack; // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri' + struct ProLoop { + QString variable; + QStringList oldVarVal; + QStringList list; + int index; + bool infinite; + }; + QStack<ProLoop> m_loopStack; // we need the following two variables for handling // CONFIG = foo bar $$CONFIG @@ -238,6 +249,7 @@ public: }; Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE); ProFileEvaluator::Private::Private(ProFileEvaluator *q_) : q(q_) @@ -618,6 +630,36 @@ void ProFileEvaluator::Private::visitEndProBlock(ProBlock *block) } } +ProItem::ProItemReturn ProFileEvaluator::Private::visitProLoopIteration() +{ + ProLoop &loop = m_loopStack.top(); + + if (loop.infinite) { + if (!loop.variable.isEmpty()) + m_valuemap[loop.variable] = QStringList(QString::number(loop.index++)); + if (loop.index > 1000) { + q->errorMessage(format("ran into infinite loop (> 1000 iterations).")); + return ProItem::ReturnFalse; + } + } else { + QString val; + do { + if (loop.index >= loop.list.count()) + return ProItem::ReturnFalse; + val = loop.list.at(loop.index++); + } while (val.isEmpty()); // stupid, but qmake is like that + m_valuemap[loop.variable] = QStringList(val); + } + return ProItem::ReturnTrue; +} + +void ProFileEvaluator::Private::visitProLoopCleanup() +{ + ProLoop &loop = m_loopStack.top(); + m_valuemap[loop.variable] = loop.oldVarVal; + m_loopStack.pop_back(); +} + void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable) { m_lastVarName = variable->variable(); @@ -1037,6 +1079,11 @@ QString ProFileEvaluator::Private::currentDirectory() const return cur->directoryName(); } +void ProFileEvaluator::Private::doVariableReplace(QString *str) +{ + *str = expandVariableReferences(*str).join(QString(Option::field_sep)); +} + QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &str) { QStringList ret; @@ -1738,7 +1785,7 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction( T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM, T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE, T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF, - T_DEFINE_TEST, T_DEFINE_REPLACE }; + T_FOR, T_DEFINE_TEST, T_DEFINE_REPLACE }; static QHash<QString, int> *functions = 0; if (!functions) { @@ -1771,6 +1818,7 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction( functions->insert(QLatin1String("message"), T_MESSAGE); //v functions->insert(QLatin1String("warning"), T_MESSAGE); //v functions->insert(QLatin1String("error"), T_MESSAGE); //v + functions->insert(QLatin1String("for"), T_FOR); //v functions->insert(QLatin1String("defineTest"), T_DEFINE_TEST); //v functions->insert(QLatin1String("defineReplace"), T_DEFINE_REPLACE); //v } @@ -1835,9 +1883,79 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction( case T_INFILE: case T_REQUIRES: case T_EVAL: +#endif + case T_FOR: { + if (m_cumulative) // This is a no-win situation, so just pretend it's no loop + return ProItem::ReturnTrue; + if (m_skipLevel) + return ProItem::ReturnFalse; + if (args.count() > 2 || args.count() < 1) { + q->logMessage(format("for({var, list|var, forever|ever})" + " requires one or two arguments.")); + return ProItem::ReturnFalse; + } + ProLoop loop; + loop.infinite = false; + loop.index = 0; + QString it_list; + if (args.count() == 1) { + doVariableReplace(&args[0]); + it_list = args[0]; + if (args[0] != QLatin1String("ever")) { + q->logMessage(format("for({var, list|var, forever|ever})" + " requires one or two arguments.")); + return ProItem::ReturnFalse; + } + it_list = QLatin1String("forever"); + } else { + loop.variable = args[0]; + loop.oldVarVal = m_valuemap.value(loop.variable); + doVariableReplace(&args[1]); + it_list = args[1]; + } + loop.list = m_valuemap[it_list]; + if (loop.list.isEmpty()) { + if (it_list == QLatin1String("forever")) { + loop.infinite = true; + } else { + int dotdot = it_list.indexOf(QLatin1String("..")); + if (dotdot != -1) { + bool ok; + int start = it_list.left(dotdot).toInt(&ok); + if (ok) { + int end = it_list.mid(dotdot+2).toInt(&ok); + if (ok) { + if (start < end) { + for (int i = start; i <= end; i++) + loop.list << QString::number(i); + } else { + for (int i = start; i >= end; i--) + loop.list << QString::number(i); + } + } + } + } + } + } + m_loopStack.push(loop); + m_sts.condition = true; + return ProItem::ReturnLoop; + } case T_BREAK: + if (m_skipLevel) + return ProItem::ReturnFalse; + if (!m_loopStack.isEmpty()) + return ProItem::ReturnBreak; + // ### missing: breaking out of multiline blocks + q->logMessage(format("unexpected break().")); + return ProItem::ReturnFalse; case T_NEXT: -#endif + if (m_skipLevel) + return ProItem::ReturnFalse; + if (!m_loopStack.isEmpty()) + return ProItem::ReturnNext; + q->logMessage(format("unexpected next().")); + return ProItem::ReturnFalse; case T_IF: { if (args.count() != 1) { q->logMessage(format("if(condition) requires one argument.")); diff --git a/src/shared/proparser/proitems.cpp b/src/shared/proparser/proitems.cpp index 3b52a64a42f0c43b284664cea25f302c6a2ddc0e..0d2a2f688039d85872d9b3765f8dace4f75da1c8 100644 --- a/src/shared/proparser/proitems.cpp +++ b/src/shared/proparser/proitems.cpp @@ -108,9 +108,30 @@ ProItem::ProItemReturn ProBlock::Accept(AbstractProItemVisitor *visitor) if (visitor->visitBeginProBlock(this) == ReturnSkip) return ReturnTrue; ProItemReturn rt = ReturnTrue; - foreach (ProItem *item, m_proitems) - if ((rt = item->Accept(visitor)) != ReturnTrue && rt != ReturnFalse) + for (int i = 0; i < m_proitems.count(); ++i) { + rt = m_proitems.at(i)->Accept(visitor); + if (rt != ReturnTrue && rt != ReturnFalse) { + if (rt == ReturnLoop) { + rt = ReturnTrue; + while (visitor->visitProLoopIteration()) + for (int j = i; ++j < m_proitems.count(); ) { + rt = m_proitems.at(j)->Accept(visitor); + if (rt != ReturnTrue && rt != ReturnFalse) { + if (rt == ReturnNext) { + rt = ReturnTrue; + break; + } + if (rt == ReturnBreak) + rt = ReturnTrue; + goto do_break; + } + } + do_break: + visitor->visitProLoopCleanup(); + } break; + } + } visitor->visitEndProBlock(this); return rt; } diff --git a/src/shared/proparser/proitems.h b/src/shared/proparser/proitems.h index b0aceef817965f71f157a1de1001a59b8fd118bb..db03eccd7730ac3dd586c6667e7d3530a8d43136 100644 --- a/src/shared/proparser/proitems.h +++ b/src/shared/proparser/proitems.h @@ -52,6 +52,9 @@ public: enum ProItemReturn { ReturnFalse, ReturnTrue, + ReturnBreak, + ReturnNext, + ReturnLoop, ReturnSkip, ReturnReturn };