summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@nokia.com>2009-05-19 19:00:06 +0200
committerOswald Buddenhagen <oswald.buddenhagen@nokia.com>2009-07-20 14:30:35 +0200
commita6ad14165e34b6c740e9d6a25c4443ed953b899b (patch)
treeb86ebbaad553a9832048e6f55404d4df1d30245f /tools
parent1aae7631bf7d98acdbe0c1e64846d96a0d914c70 (diff)
support loops: implement for(), next() & break()
cherry-picked 88de3e6a45a41baecb7e56e7cbab7fec30ac0a1c from creator
Diffstat (limited to 'tools')
-rw-r--r--tools/linguist/shared/abstractproitemvisitor.h3
-rw-r--r--tools/linguist/shared/profileevaluator.cpp122
-rw-r--r--tools/linguist/shared/proitems.cpp25
-rw-r--r--tools/linguist/shared/proitems.h3
4 files changed, 149 insertions, 4 deletions
diff --git a/tools/linguist/shared/abstractproitemvisitor.h b/tools/linguist/shared/abstractproitemvisitor.h
index dd72dfe09f..43e79e0852 100644
--- a/tools/linguist/shared/abstractproitemvisitor.h
+++ b/tools/linguist/shared/abstractproitemvisitor.h
@@ -53,6 +53,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/tools/linguist/shared/profileevaluator.cpp b/tools/linguist/shared/profileevaluator.cpp
index daf75c8f1f..d250217d8c 100644
--- a/tools/linguist/shared/profileevaluator.cpp
+++ b/tools/linguist/shared/profileevaluator.cpp
@@ -173,6 +173,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);
@@ -191,6 +193,7 @@ public:
bool isActiveConfig(const QString &config, bool regex = false);
QStringList expandVariableReferences(const QString &value);
+ void doVariableReplace(QString *str);
QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
QString format(const char *format) const;
@@ -222,6 +225,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
@@ -247,6 +258,7 @@ public:
#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE);
#endif
ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
@@ -627,6 +639,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();
@@ -1017,6 +1059,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;
@@ -1720,7 +1767,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) {
@@ -1753,6 +1800,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
}
@@ -1817,9 +1865,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/tools/linguist/shared/proitems.cpp b/tools/linguist/shared/proitems.cpp
index 0d9f5c4303..905c67e62e 100644
--- a/tools/linguist/shared/proitems.cpp
+++ b/tools/linguist/shared/proitems.cpp
@@ -120,9 +120,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/tools/linguist/shared/proitems.h b/tools/linguist/shared/proitems.h
index be086acde2..7833be1888 100644
--- a/tools/linguist/shared/proitems.h
+++ b/tools/linguist/shared/proitems.h
@@ -63,6 +63,9 @@ public:
enum ProItemReturn {
ReturnFalse,
ReturnTrue,
+ ReturnBreak,
+ ReturnNext,
+ ReturnLoop,
ReturnSkip,
ReturnReturn
};