support loops: implement for(), next() & break()

This commit is contained in:
Oswald Buddenhagen
2009-05-19 19:00:06 +02:00
parent d89338aa81
commit 88de3e6a45
4 changed files with 150 additions and 5 deletions

View File

@@ -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;

View File

@@ -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."));

View File

@@ -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;
}
if (rt == ReturnBreak)
rt = ReturnTrue;
goto do_break;
}
}
do_break:
visitor->visitProLoopCleanup();
}
break; break;
}
}
visitor->visitEndProBlock(this); visitor->visitEndProBlock(this);
return rt; return rt;
} }

View File

@@ -52,6 +52,9 @@ public:
enum ProItemReturn { enum ProItemReturn {
ReturnFalse, ReturnFalse,
ReturnTrue, ReturnTrue,
ReturnBreak,
ReturnNext,
ReturnLoop,
ReturnSkip, ReturnSkip,
ReturnReturn ReturnReturn
}; };