Commit f53ed6c4 authored by Oswald Buddenhagen's avatar Oswald Buddenhagen
Browse files

don't pretend that break()/next()/return() are functions



it's a pretty braindead thing to implement control flow statements as
(built-in) functions.

as a "side effect", this fixes return() value handling for lists.

Change-Id: I59c8efa0e4d65329327115f7f8ed20719e7f7546
Reviewed-by: default avatarDaniel Teske <daniel.teske@nokia.com>
Reviewed-by: default avatarOswald Buddenhagen <oswald.buddenhagen@nokia.com>
parent 00c0d0d5
...@@ -286,6 +286,9 @@ enum ProToken { ...@@ -286,6 +286,9 @@ enum ProToken {
TokTestCall, // previous literal/expansion is a test function call TokTestCall, // previous literal/expansion is a test function call
// - ((nested expansion + TokArgSeparator)* + nested expansion)? // - ((nested expansion + TokArgSeparator)* + nested expansion)?
// - TokFuncTerminator // - TokFuncTerminator
TokReturn, // previous literal/expansion is a return value
TokBreak, // break loop
TokNext, // shortcut to next loop iteration
TokNot, // '!' operator TokNot, // '!' operator
TokAnd, // ':' operator TokAnd, // ':' operator
TokOr, // '|' operator TokOr, // '|' operator
......
...@@ -159,6 +159,9 @@ static const ushort *skipToken(ushort tok, const ushort *&tokPtr, int &lineNo) ...@@ -159,6 +159,9 @@ static const ushort *skipToken(ushort tok, const ushort *&tokPtr, int &lineNo)
case TokAnd: case TokAnd:
case TokOr: case TokOr:
case TokCondition: case TokCondition:
case TokReturn:
case TokNext:
case TokBreak:
break; break;
default: { default: {
const ushort *oTokPtr = --tokPtr; const ushort *oTokPtr = --tokPtr;
......
...@@ -85,7 +85,7 @@ enum ExpandFunc { ...@@ -85,7 +85,7 @@ enum ExpandFunc {
enum TestFunc { enum TestFunc {
T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS, T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
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_DEFINED, T_CONTAINS, T_INFILE,
T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF, T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF,
T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE
}; };
...@@ -157,9 +157,6 @@ void QMakeEvaluator::initFunctionStatics() ...@@ -157,9 +157,6 @@ void QMakeEvaluator::initFunctionStatics()
{ "if", T_IF }, { "if", T_IF },
{ "isActiveConfig", T_CONFIG }, { "isActiveConfig", T_CONFIG },
{ "system", T_SYSTEM }, { "system", T_SYSTEM },
{ "return", T_RETURN },
{ "break", T_BREAK },
{ "next", T_NEXT },
{ "defined", T_DEFINED }, { "defined", T_DEFINED },
{ "contains", T_CONTAINS }, { "contains", T_CONTAINS },
{ "infile", T_INFILE }, { "infile", T_INFILE },
...@@ -1085,17 +1082,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction( ...@@ -1085,17 +1082,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
return returnBool(m_functionDefs.replaceFunctions.contains(var) return returnBool(m_functionDefs.replaceFunctions.contains(var)
|| m_functionDefs.testFunctions.contains(var)); || m_functionDefs.testFunctions.contains(var));
} }
case T_RETURN:
m_returnValue = args;
// It is "safe" to ignore returns - due to qmake brokeness
// they cannot be used to terminate loops anyway.
if (m_cumulative)
return ReturnTrue;
if (m_valuemapStack.size() == 1) {
evalError(fL1S("unexpected return()."));
return ReturnFalse;
}
return ReturnReturn;
case T_EXPORT: { case T_EXPORT: {
if (args.count() != 1) { if (args.count() != 1) {
evalError(fL1S("export(variable) requires one argument.")); evalError(fL1S("export(variable) requires one argument."));
...@@ -1165,20 +1151,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction( ...@@ -1165,20 +1151,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
} }
return ret; return ret;
} }
case T_BREAK:
if (m_skipLevel)
return ReturnFalse;
if (m_loopLevel)
return ReturnBreak;
evalError(fL1S("Unexpected break()."));
return ReturnFalse;
case T_NEXT:
if (m_skipLevel)
return ReturnFalse;
if (m_loopLevel)
return ReturnNext;
evalError(fL1S("Unexpected next()."));
return ReturnFalse;
case T_IF: { case T_IF: {
if (args.count() != 1) { if (args.count() != 1) {
evalError(fL1S("if(condition) requires one argument.")); evalError(fL1S("if(condition) requires one argument."));
......
...@@ -180,7 +180,6 @@ QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option, ...@@ -180,7 +180,6 @@ QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option,
#ifdef PROEVALUATOR_CUMULATIVE #ifdef PROEVALUATOR_CUMULATIVE
m_skipLevel = 0; m_skipLevel = 0;
#endif #endif
m_loopLevel = 0;
m_listCount = 0; m_listCount = 0;
m_valuemapStack.push(ProValueMap()); m_valuemapStack.push(ProValueMap());
m_valuemapInited = false; m_valuemapInited = false;
...@@ -630,6 +629,21 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( ...@@ -630,6 +629,21 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
invert = false; invert = false;
curr.clear(); curr.clear();
continue; continue;
case TokReturn:
m_returnValue = curr;
curr.clear();
ret = ReturnReturn;
goto ctrlstm;
case TokBreak:
ret = ReturnBreak;
goto ctrlstm;
case TokNext:
ret = ReturnNext;
ctrlstm:
if (!m_skipLevel && okey != or_op)
return ret;
okey = false, or_op = true; // force next evaluation
continue;
default: { default: {
const ushort *oTokPtr = --tokPtr; const ushort *oTokPtr = --tokPtr;
evaluateExpression(tokPtr, &curr, false); evaluateExpression(tokPtr, &curr, false);
...@@ -701,7 +715,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop( ...@@ -701,7 +715,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop(
} }
} }
m_loopLevel++;
forever { forever {
if (infinite) { if (infinite) {
if (!variable.isEmpty()) if (!variable.isEmpty())
...@@ -736,7 +749,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop( ...@@ -736,7 +749,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop(
} }
} }
do_break: do_break:
m_loopLevel--;
if (!variable.isEmpty()) if (!variable.isEmpty())
m_valuemapStack.top()[variable] = oldVarVal; m_valuemapStack.top()[variable] = oldVarVal;
...@@ -1477,8 +1489,6 @@ ProStringList QMakeEvaluator::evaluateFunction( ...@@ -1477,8 +1489,6 @@ ProStringList QMakeEvaluator::evaluateFunction(
} else { } else {
m_valuemapStack.push(ProValueMap()); m_valuemapStack.push(ProValueMap());
m_locationStack.push(m_current); m_locationStack.push(m_current);
int loopLevel = m_loopLevel;
m_loopLevel = 0;
ProStringList args; ProStringList args;
for (int i = 0; i < argumentsList.count(); ++i) { for (int i = 0; i < argumentsList.count(); ++i) {
...@@ -1491,7 +1501,6 @@ ProStringList QMakeEvaluator::evaluateFunction( ...@@ -1491,7 +1501,6 @@ ProStringList QMakeEvaluator::evaluateFunction(
ret = m_returnValue; ret = m_returnValue;
m_returnValue.clear(); m_returnValue.clear();
m_loopLevel = loopLevel;
m_current = m_locationStack.pop(); m_current = m_locationStack.pop();
m_valuemapStack.pop(); m_valuemapStack.pop();
} }
......
...@@ -197,7 +197,6 @@ public: ...@@ -197,7 +197,6 @@ public:
static void removeEach(ProStringList *varlist, const ProStringList &value); static void removeEach(ProStringList *varlist, const ProStringList &value);
QMakeEvaluator *m_caller; QMakeEvaluator *m_caller;
int m_loopLevel; // To report unexpected break() and next()s
#ifdef PROEVALUATOR_CUMULATIVE #ifdef PROEVALUATOR_CUMULATIVE
bool m_cumulative; bool m_cumulative;
int m_skipLevel; int m_skipLevel;
......
...@@ -97,6 +97,9 @@ static struct { ...@@ -97,6 +97,9 @@ static struct {
QString strdefineTest; QString strdefineTest;
QString strdefineReplace; QString strdefineReplace;
QString stroption; QString stroption;
QString strreturn;
QString strnext;
QString strbreak;
QString strhost_build; QString strhost_build;
QString strLINE; QString strLINE;
QString strFILE; QString strFILE;
...@@ -117,6 +120,9 @@ void QMakeParser::initialize() ...@@ -117,6 +120,9 @@ void QMakeParser::initialize()
statics.strdefineTest = QLatin1String("defineTest"); statics.strdefineTest = QLatin1String("defineTest");
statics.strdefineReplace = QLatin1String("defineReplace"); statics.strdefineReplace = QLatin1String("defineReplace");
statics.stroption = QLatin1String("option"); statics.stroption = QLatin1String("option");
statics.strreturn = QLatin1String("return");
statics.strnext = QLatin1String("next");
statics.strbreak = QLatin1String("break");
statics.strhost_build = QLatin1String("host_build"); statics.strhost_build = QLatin1String("host_build");
statics.strLINE = QLatin1String("_LINE_"); statics.strLINE = QLatin1String("_LINE_");
statics.strFILE = QLatin1String("_FILE_"); statics.strFILE = QLatin1String("_FILE_");
...@@ -836,9 +842,11 @@ void QMakeParser::putLineMarker(ushort *&tokPtr) ...@@ -836,9 +842,11 @@ void QMakeParser::putLineMarker(ushort *&tokPtr)
void QMakeParser::enterScope(ushort *&tokPtr, bool special, ScopeState state) void QMakeParser::enterScope(ushort *&tokPtr, bool special, ScopeState state)
{ {
uchar nest = m_blockstack.top().nest;
m_blockstack.resize(m_blockstack.size() + 1); m_blockstack.resize(m_blockstack.size() + 1);
m_blockstack.top().special = special; m_blockstack.top().special = special;
m_blockstack.top().start = tokPtr; m_blockstack.top().start = tokPtr;
m_blockstack.top().nest = nest;
tokPtr += 2; tokPtr += 2;
m_state = state; m_state = state;
m_canElse = false; m_canElse = false;
...@@ -978,6 +986,7 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg ...@@ -978,6 +986,7 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg
m_tmp.setRawData((QChar *)uc + 4, nlen); m_tmp.setRawData((QChar *)uc + 4, nlen);
const QString *defName; const QString *defName;
ushort defType; ushort defType;
uchar nest;
if (m_tmp == statics.strfor) { if (m_tmp == statics.strfor) {
if (m_invert || m_operator == OrOperator) { if (m_invert || m_operator == OrOperator) {
// '|' could actually work reasonably, but qmake does nonsense here. // '|' could actually work reasonably, but qmake does nonsense here.
...@@ -1000,6 +1009,7 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg ...@@ -1000,6 +1009,7 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg
didFor: didFor:
putTok(tokPtr, TokValueTerminator); putTok(tokPtr, TokValueTerminator);
enterScope(tokPtr, true, StCtrl); enterScope(tokPtr, true, StCtrl);
m_blockstack.top().nest |= NestLoop;
return; return;
} else if (*uc == TokArgSeparator && argc == 2) { } else if (*uc == TokArgSeparator && argc == 2) {
// for(var, something) // for(var, something)
...@@ -1046,11 +1056,48 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg ...@@ -1046,11 +1056,48 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg
putTok(tokPtr, defType); putTok(tokPtr, defType);
putHashStr(tokPtr, uce + 2, nlen); putHashStr(tokPtr, uce + 2, nlen);
enterScope(tokPtr, true, StCtrl); enterScope(tokPtr, true, StCtrl);
m_blockstack.top().nest = NestFunction;
return; return;
} }
} }
parseError(fL1S("%1(function) requires one literal argument.").arg(*defName)); parseError(fL1S("%1(function) requires one literal argument.").arg(*defName));
return; return;
} else if (m_tmp == statics.strreturn) {
if (argc > 1) {
parseError(fL1S("return() requires zero or one argument."));
bogusTest(tokPtr);
return;
}
defType = TokReturn;
nest = NestFunction;
goto ctrlstm2;
} else if (m_tmp == statics.strnext) {
defType = TokNext;
goto ctrlstm;
} else if (m_tmp == statics.strbreak) {
defType = TokBreak;
ctrlstm:
if (*uce != TokFuncTerminator) {
parseError(fL1S("%1() requires zero arguments.").arg(m_tmp));
bogusTest(tokPtr);
return;
}
nest = NestLoop;
ctrlstm2:
if (m_invert) {
parseError(fL1S("Unexpected NOT operator in front of %1().").arg(m_tmp));
bogusTest(tokPtr);
return;
}
if (!(m_blockstack.top().nest & nest)) {
parseError(fL1S("Unexpected %1().").arg(m_tmp));
bogusTest(tokPtr);
return;
}
finalizeTest(tokPtr);
putBlock(tokPtr, uce, ptr - uce - 1); // Only for TokReturn
putTok(tokPtr, defType);
return;
} else if (m_tmp == statics.stroption) { } else if (m_tmp == statics.stroption) {
if (m_state != StNew || m_blockstack.top().braceLevel || m_blockstack.size() > 1 if (m_state != StNew || m_blockstack.top().braceLevel || m_blockstack.size() > 1
|| m_invert || m_operator != NoOperator) { || m_invert || m_operator != NoOperator) {
......
...@@ -83,13 +83,20 @@ public: ...@@ -83,13 +83,20 @@ public:
SubGrammar grammar = FullGrammar); SubGrammar grammar = FullGrammar);
private: private:
enum ScopeNesting {
NestNone = 0,
NestLoop = 1,
NestFunction = 2
};
struct BlockScope { struct BlockScope {
BlockScope() : start(0), braceLevel(0), special(false), inBranch(false) {} BlockScope() : start(0), braceLevel(0), special(false), inBranch(false), nest(NestNone) {}
BlockScope(const BlockScope &other) { *this = other; } BlockScope(const BlockScope &other) { *this = other; }
ushort *start; // Where this block started; store length here ushort *start; // Where this block started; store length here
int braceLevel; // Nesting of braces in scope int braceLevel; // Nesting of braces in scope
bool special; // Single-line conditionals inside loops, etc. cannot have else branches bool special; // Single-line conditionals inside loops, etc. cannot have else branches
bool inBranch; // The 'else' branch of the previous TokBranch is still open bool inBranch; // The 'else' branch of the previous TokBranch is still open
uchar nest; // Into what control structures we are nested
}; };
enum ScopeState { enum ScopeState {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment