forked from qt-creator/qt-creator
support loops: implement for(), next() & break()
This commit is contained in:
@@ -41,6 +41,9 @@ struct AbstractProItemVisitor
|
|||||||
virtual ProItem::ProItemReturn visitBeginProBlock(ProBlock *block) = 0;
|
virtual ProItem::ProItemReturn visitBeginProBlock(ProBlock *block) = 0;
|
||||||
virtual void visitEndProBlock(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 visitBeginProVariable(ProVariable *variable) = 0;
|
||||||
virtual void visitEndProVariable(ProVariable *variable) = 0;
|
virtual void visitEndProVariable(ProVariable *variable) = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,8 @@ public:
|
|||||||
// implementation of AbstractProItemVisitor
|
// implementation of AbstractProItemVisitor
|
||||||
ProItem::ProItemReturn visitBeginProBlock(ProBlock *block);
|
ProItem::ProItemReturn visitBeginProBlock(ProBlock *block);
|
||||||
void visitEndProBlock(ProBlock *block);
|
void visitEndProBlock(ProBlock *block);
|
||||||
|
ProItem::ProItemReturn visitProLoopIteration();
|
||||||
|
void visitProLoopCleanup();
|
||||||
void visitBeginProVariable(ProVariable *variable);
|
void visitBeginProVariable(ProVariable *variable);
|
||||||
void visitEndProVariable(ProVariable *variable);
|
void visitEndProVariable(ProVariable *variable);
|
||||||
ProItem::ProItemReturn visitBeginProFile(ProFile *value);
|
ProItem::ProItemReturn visitBeginProFile(ProFile *value);
|
||||||
@@ -180,6 +182,7 @@ public:
|
|||||||
void expandPatternHelper(const QString &relName, const QString &absName,
|
void expandPatternHelper(const QString &relName, const QString &absName,
|
||||||
QStringList &sources_out);
|
QStringList &sources_out);
|
||||||
QStringList expandVariableReferences(const QString &value);
|
QStringList expandVariableReferences(const QString &value);
|
||||||
|
void doVariableReplace(QString *str);
|
||||||
QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
|
QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
|
||||||
QString format(const char *format) const;
|
QString format(const char *format) const;
|
||||||
|
|
||||||
@@ -211,6 +214,14 @@ public:
|
|||||||
QString m_origfile;
|
QString m_origfile;
|
||||||
QString m_oldPath; // To restore the current path to the path
|
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'
|
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
|
// we need the following two variables for handling
|
||||||
// CONFIG = foo bar $$CONFIG
|
// CONFIG = foo bar $$CONFIG
|
||||||
@@ -238,6 +249,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
|
Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
|
||||||
|
Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE);
|
||||||
|
|
||||||
ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
|
ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
|
||||||
: q(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)
|
void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
|
||||||
{
|
{
|
||||||
m_lastVarName = variable->variable();
|
m_lastVarName = variable->variable();
|
||||||
@@ -1037,6 +1079,11 @@ QString ProFileEvaluator::Private::currentDirectory() const
|
|||||||
return cur->directoryName();
|
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 ProFileEvaluator::Private::expandVariableReferences(const QString &str)
|
||||||
{
|
{
|
||||||
QStringList ret;
|
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_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_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_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;
|
static QHash<QString, int> *functions = 0;
|
||||||
if (!functions) {
|
if (!functions) {
|
||||||
@@ -1771,6 +1818,7 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
|
|||||||
functions->insert(QLatin1String("message"), T_MESSAGE); //v
|
functions->insert(QLatin1String("message"), T_MESSAGE); //v
|
||||||
functions->insert(QLatin1String("warning"), T_MESSAGE); //v
|
functions->insert(QLatin1String("warning"), T_MESSAGE); //v
|
||||||
functions->insert(QLatin1String("error"), 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("defineTest"), T_DEFINE_TEST); //v
|
||||||
functions->insert(QLatin1String("defineReplace"), T_DEFINE_REPLACE); //v
|
functions->insert(QLatin1String("defineReplace"), T_DEFINE_REPLACE); //v
|
||||||
}
|
}
|
||||||
@@ -1835,9 +1883,79 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
|
|||||||
case T_INFILE:
|
case T_INFILE:
|
||||||
case T_REQUIRES:
|
case T_REQUIRES:
|
||||||
case T_EVAL:
|
case T_EVAL:
|
||||||
case T_BREAK:
|
|
||||||
case T_NEXT:
|
|
||||||
#endif
|
#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:
|
||||||
|
if (m_skipLevel)
|
||||||
|
return ProItem::ReturnFalse;
|
||||||
|
if (!m_loopStack.isEmpty())
|
||||||
|
return ProItem::ReturnNext;
|
||||||
|
q->logMessage(format("unexpected next()."));
|
||||||
|
return ProItem::ReturnFalse;
|
||||||
case T_IF: {
|
case T_IF: {
|
||||||
if (args.count() != 1) {
|
if (args.count() != 1) {
|
||||||
q->logMessage(format("if(condition) requires one argument."));
|
q->logMessage(format("if(condition) requires one argument."));
|
||||||
|
|||||||
@@ -108,9 +108,30 @@ ProItem::ProItemReturn ProBlock::Accept(AbstractProItemVisitor *visitor)
|
|||||||
if (visitor->visitBeginProBlock(this) == ReturnSkip)
|
if (visitor->visitBeginProBlock(this) == ReturnSkip)
|
||||||
return ReturnTrue;
|
return ReturnTrue;
|
||||||
ProItemReturn rt = ReturnTrue;
|
ProItemReturn rt = ReturnTrue;
|
||||||
foreach (ProItem *item, m_proitems)
|
for (int i = 0; i < m_proitems.count(); ++i) {
|
||||||
if ((rt = item->Accept(visitor)) != ReturnTrue && rt != ReturnFalse)
|
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;
|
break;
|
||||||
|
}
|
||||||
|
if (rt == ReturnBreak)
|
||||||
|
rt = ReturnTrue;
|
||||||
|
goto do_break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do_break:
|
||||||
|
visitor->visitProLoopCleanup();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
visitor->visitEndProBlock(this);
|
visitor->visitEndProBlock(this);
|
||||||
return rt;
|
return rt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ public:
|
|||||||
enum ProItemReturn {
|
enum ProItemReturn {
|
||||||
ReturnFalse,
|
ReturnFalse,
|
||||||
ReturnTrue,
|
ReturnTrue,
|
||||||
|
ReturnBreak,
|
||||||
|
ReturnNext,
|
||||||
|
ReturnLoop,
|
||||||
ReturnSkip,
|
ReturnSkip,
|
||||||
ReturnReturn
|
ReturnReturn
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user