don't pre-split RHS of variable assignments

creating tons of items is a tad allocation-intensive and thus slow.

this is probably slower for often-included files, as now the splitting
is done in every evaluation pass.
This commit is contained in:
Oswald Buddenhagen
2010-01-14 12:34:46 +01:00
parent 61772da250
commit 9c7d803aec
5 changed files with 158 additions and 267 deletions

View File

@@ -44,13 +44,10 @@ struct AbstractProItemVisitor
virtual ProItem::ProItemReturn visitProLoopIteration() = 0;
virtual void visitProLoopCleanup() = 0;
virtual void visitBeginProVariable(ProVariable *variable) = 0;
virtual void visitEndProVariable(ProVariable *variable) = 0;
virtual ProItem::ProItemReturn visitBeginProFile(ProFile *value) = 0;
virtual ProItem::ProItemReturn visitEndProFile(ProFile *value) = 0;
virtual void visitProValue(ProValue *value) = 0;
virtual void visitProVariable(ProVariable *variable) = 0;
virtual ProItem::ProItemReturn visitProFunction(ProFunction *function) = 0;
virtual void visitProOperator(ProOperator *function) = 0;
virtual void visitProCondition(ProCondition *function) = 0;

View File

@@ -196,7 +196,8 @@ public:
ProBlock *currentBlock();
void updateItem(ushort *ptr);
void updateItem2();
bool insertVariable(ushort *ptr, bool *doSplit, bool *doSemicolon);
ProVariable *startVariable(ushort *ptr);
void finalizeVariable(ProVariable *var);
void insertOperator(const char op);
void enterScope(bool multiLine);
void leaveScope();
@@ -214,11 +215,9 @@ public:
void visitEndProBlock(ProBlock *block);
ProItem::ProItemReturn visitProLoopIteration();
void visitProLoopCleanup();
void visitBeginProVariable(ProVariable *variable);
void visitEndProVariable(ProVariable *variable);
ProItem::ProItemReturn visitBeginProFile(ProFile *value);
ProItem::ProItemReturn visitEndProFile(ProFile *value);
void visitProValue(ProValue *value);
void visitProVariable(ProVariable *variable);
ProItem::ProItemReturn visitProFunction(ProFunction *function);
void visitProOperator(ProOperator *oper);
void visitProCondition(ProCondition *condition);
@@ -230,8 +229,8 @@ public:
const ProFile *pro) const;
QString propertyValue(const QString &val, bool complain = true) const;
QStringList split_value_list(const QString &vals, bool do_semicolon = false);
QStringList split_arg_list(const QString &params);
static QStringList split_value_list(const QString &vals, bool do_semicolon = false);
static QStringList split_arg_list(const QString &params);
bool isActiveConfig(const QString &config, bool regex = false);
QStringList expandVariableReferences(const QString &value);
void doVariableReplace(QString *str);
@@ -266,7 +265,6 @@ public:
struct State {
bool condition;
bool prevCondition;
QStringList varVal;
} m_sts;
bool m_invertNext; // Short-lived, so not in State
bool m_hadCondition; // Nested calls set it on return, so no need for it to be in State
@@ -358,11 +356,9 @@ bool ProFileEvaluator::Private::read(ProBlock *pro, QTextStream *ts)
m_blockstack.push(pro);
freshLine:
ProVariable *currAssignment = 0;
int parens = 0;
bool inError = false;
bool inAssignment = false;
bool doSplit = false;
bool doSemicolon = false;
bool putSpace = false;
ushort quote = 0;
while (!ts->atEnd()) {
@@ -374,7 +370,12 @@ bool ProFileEvaluator::Private::read(ProBlock *pro, QTextStream *ts)
// First, skip leading whitespace
forever {
if (cur == end) { // Entirely empty line (sans whitespace)
if (currAssignment) {
finalizeVariable(currAssignment);
currAssignment = 0;
} else {
updateItem2();
}
finalizeBlock();
++m_lineNo;
goto freshLine;
@@ -419,7 +420,7 @@ bool ProFileEvaluator::Private::read(ProBlock *pro, QTextStream *ts)
m_proitem.reserve(m_proitem.length() + (end - cur));
// Finally, do the tokenization
if (!inAssignment) {
if (!currAssignment) {
newItem:
ptr = (ushort *)m_proitem.unicode() + m_proitem.length();
do {
@@ -454,8 +455,7 @@ bool ProFileEvaluator::Private::read(ProBlock *pro, QTextStream *ts)
goto nextItem;
}
if (c == '=') {
if (insertVariable(ptr, &doSplit, &doSemicolon)) {
inAssignment = true;
if ((currAssignment = startVariable(ptr))) {
putSpace = false;
break;
}
@@ -485,73 +485,14 @@ bool ProFileEvaluator::Private::read(ProBlock *pro, QTextStream *ts)
putSpace = true;
}
}
} // !inAssignment
} // !currAssignment
nextVal:
ptr = (ushort *)m_proitem.unicode() + m_proitem.length();
do {
if (cur == end)
goto lineEnd;
c = *cur++;
} while (c == ' ' || c == '\t');
if (doSplit) {
// Qmake's parser supports truly bizarre quote nesting here, but later
// stages (in qmake) don't grok it anyway. So make it simple instead.
forever {
if (c == '\\') {
ushort ec;
if (cur != end && ((ec = *cur) == '"' || ec == '\'')) {
++cur;
if (putSpace) {
putSpace = false;
*ptr++ = ' ';
}
*ptr++ = '\\';
*ptr++ = ec;
goto getNext;
}
} else {
if (quote) {
if (c == quote) {
quote = 0;
} else if (c == ' ' || c == '\t') {
putSpace = true;
goto getNext;
}
} else {
if (c == '"' || c == '\'') {
quote = c;
} else if (c == ')') {
--parens;
} else if (c == '(') {
++parens;
} else if (c == ' ' || c == '\t') {
if (parens) {
putSpace = true;
goto getNext;
}
updateItem(ptr);
// assert(!putSpace);
goto nextVal;
}
}
}
if (putSpace) {
putSpace = false;
*ptr++ = ' ';
}
*ptr++ = c;
getNext:
if (cur == end) {
if (!quote && !parens)
goto flushItem;
break;
}
c = *cur++;
}
} else { // doSplit
forever {
if (putSpace) {
putSpace = false;
@@ -568,14 +509,17 @@ bool ProFileEvaluator::Private::read(ProBlock *pro, QTextStream *ts)
putSpace = true;
}
}
}
lineEnd:
if (lineCont) {
m_proitem.resize(ptr - (ushort *)m_proitem.unicode());
if (lineCont) {
putSpace = !m_proitem.isEmpty();
} else {
flushItem:
updateItem(ptr);
if (currAssignment) {
finalizeVariable(currAssignment);
currAssignment = 0;
} else {
updateItem2();
}
putSpace = false;
}
} // !inError
@@ -599,13 +543,13 @@ void ProFileEvaluator::Private::finalizeBlock()
m_block = 0;
}
bool ProFileEvaluator::Private::insertVariable(ushort *ptr, bool *doSplit, bool *doSemicolon)
ProVariable *ProFileEvaluator::Private::startVariable(ushort *ptr)
{
ProVariable::VariableOperator opkind;
ushort *uc = (ushort *)m_proitem.unicode();
if (ptr == uc) // Line starting with '=', like a conflict marker
return false;
return 0;
switch (*(ptr - 1)) {
case '+':
@@ -630,7 +574,7 @@ bool ProFileEvaluator::Private::insertVariable(ushort *ptr, bool *doSplit, bool
}
if (ptr == uc) // Line starting with manipulation operator
return false;
return 0;
if (*(ptr - 1) == ' ')
--ptr;
@@ -638,20 +582,23 @@ bool ProFileEvaluator::Private::insertVariable(ushort *ptr, bool *doSplit, bool
m_proitem.resize(ptr - uc);
QString proVar = m_proitem;
proVar.detach();
ProBlock *block = m_blockstack.top();
ProVariable *variable = new ProVariable(proVar, block);
variable->setLineNumber(m_lineNo);
variable->setVariableOperator(opkind);
block->appendItem(variable);
m_block = variable;
m_proitem.resize(0);
*doSplit = (opkind != ProVariable::ReplaceOperator);
*doSemicolon = (proVar == QLatin1String("DEPENDPATH")
|| proVar == QLatin1String("INCLUDEPATH"));
return true;
ProVariable *variable = new ProVariable(proVar);
variable->setLineNumber(m_lineNo);
variable->setVariableOperator(opkind);
return variable;
}
void ProFileEvaluator::Private::finalizeVariable(ProVariable *variable)
{
QString proItem = m_proitem;
proItem.detach();
m_proitem.resize(0);
variable->setValue(proItem);
ProBlock *block = m_blockstack.top();
block->appendItem(variable);
}
void ProFileEvaluator::Private::insertOperator(const char op)
@@ -730,9 +677,7 @@ void ProFileEvaluator::Private::updateItem2()
ProBlock *block = currentBlock();
ProItem *item;
if (block->blockKind() & ProBlock::VariableKind) {
item = new ProValue(proItem, static_cast<ProVariable*>(block));
} else if (proItem.endsWith(QLatin1Char(')'))) {
if (proItem.endsWith(QLatin1Char(')'))) {
item = new ProFunction(proItem);
} else {
item = new ProCondition(proItem);
@@ -1001,65 +946,25 @@ void ProFileEvaluator::Private::visitProLoopCleanup()
m_loopStack.pop_back();
}
void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *)
void ProFileEvaluator::Private::visitProVariable(ProVariable *var)
{
m_sts.varVal.clear();
}
m_lineNo = var->lineNumber();
const QString &varName = var->variable();
void ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
{
QString varName = variable->variable();
switch (variable->variableOperator()) {
case ProVariable::SetOperator: // =
if (!m_cumulative) {
if (!m_skipLevel) {
m_valuemap[varName] = m_sts.varVal;
m_filevaluemap[currentProFile()][varName] = m_sts.varVal;
}
} else {
// We are greedy for values.
m_valuemap[varName] += m_sts.varVal;
m_filevaluemap[currentProFile()][varName] += m_sts.varVal;
}
break;
case ProVariable::UniqueAddOperator: // *=
if (!m_skipLevel || m_cumulative) {
insertUnique(&m_valuemap, varName, m_sts.varVal);
insertUnique(&m_filevaluemap[currentProFile()], varName, m_sts.varVal);
}
break;
case ProVariable::AddOperator: // +=
if (!m_skipLevel || m_cumulative) {
m_valuemap[varName] += m_sts.varVal;
m_filevaluemap[currentProFile()][varName] += m_sts.varVal;
}
break;
case ProVariable::RemoveOperator: // -=
if (!m_cumulative) {
if (!m_skipLevel) {
removeEach(&m_valuemap, varName, m_sts.varVal);
removeEach(&m_filevaluemap[currentProFile()], varName, m_sts.varVal);
}
} else {
// We are stingy with our values, too.
}
break;
case ProVariable::ReplaceOperator: // ~=
{
if (var->variableOperator() == ProVariable::ReplaceOperator) { // ~=
// DEFINES ~= s/a/b/?[gqi]
QString val = m_sts.varVal.first();
QString val = var->value();
doVariableReplace(&val);
if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
logMessage(format("the ~= operator can handle only the s/// function."));
break;
return;
}
QChar sep = val.at(1);
QStringList func = val.split(sep);
if (func.count() < 3 || func.count() > 4) {
logMessage(format("the s/// function expects 3 or 4 arguments."));
break;
return;
}
bool global = false, quote = false, case_sense = false;
@@ -1080,10 +985,52 @@ void ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
// but this will break just as much as it fixes, so leave it as is.
replaceInList(&m_valuemap[varName], regexp, replace, global);
replaceInList(&m_filevaluemap[currentProFile()][varName], regexp, replace, global);
}
} else {
static const QString deppath(QLatin1String("DEPENDPATH"));
static const QString incpath(QLatin1String("INCLUDEPATH"));
bool doSemicolon = (varName == deppath || varName == incpath);
QStringList varVal;
foreach (const QString &arg, split_value_list(var->value(), doSemicolon))
varVal += expandVariableReferences(arg);
switch (var->variableOperator()) {
default: // ReplaceOperator - cannot happen
case ProVariable::SetOperator: // =
if (!m_cumulative) {
if (!m_skipLevel) {
m_valuemap[varName] = varVal;
m_filevaluemap[currentProFile()][varName] = varVal;
}
} else {
// We are greedy for values.
m_valuemap[varName] += varVal;
m_filevaluemap[currentProFile()][varName] += varVal;
}
break;
case ProVariable::UniqueAddOperator: // *=
if (!m_skipLevel || m_cumulative) {
insertUnique(&m_valuemap, varName, varVal);
insertUnique(&m_filevaluemap[currentProFile()], varName, varVal);
}
break;
case ProVariable::AddOperator: // +=
if (!m_skipLevel || m_cumulative) {
m_valuemap[varName] += varVal;
m_filevaluemap[currentProFile()][varName] += varVal;
}
break;
case ProVariable::RemoveOperator: // -=
if (!m_cumulative) {
if (!m_skipLevel) {
removeEach(&m_valuemap, varName, varVal);
removeEach(&m_filevaluemap[currentProFile()], varName, varVal);
}
} else {
// We are stingy with our values, too.
}
break;
}
}
}
@@ -1266,12 +1213,6 @@ ProItem::ProItemReturn ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
return returnBool(QDir::setCurrent(m_oldPathStack.pop()));
}
void ProFileEvaluator::Private::visitProValue(ProValue *value)
{
m_lineNo = value->lineNumber();
m_sts.varVal += expandVariableReferences(value->value());
}
ProItem::ProItemReturn ProFileEvaluator::Private::visitProFunction(ProFunction *func)
{
// Make sure that called subblocks don't inherit & destroy the state

View File

@@ -126,10 +126,8 @@ ProItem::ProItemReturn ProBlock::Accept(AbstractProItemVisitor *visitor)
}
// --------------- ProVariable ----------------
ProVariable::ProVariable(const QString &name, ProBlock *parent)
: ProBlock(parent)
ProVariable::ProVariable(const QString &name)
{
setBlockKind(ProBlock::VariableKind);
m_variable = name;
m_variableKind = SetOperator;
}
@@ -154,50 +152,24 @@ QString ProVariable::variable() const
return m_variable;
}
ProItem::ProItemReturn ProVariable::Accept(AbstractProItemVisitor *visitor)
void ProVariable::setValue(const QString &val)
{
visitor->visitBeginProVariable(this);
foreach (ProItem *item, m_proitems)
item->Accept(visitor); // cannot fail
visitor->visitEndProVariable(this);
return ReturnTrue;
m_value = val;
}
// --------------- ProValue ----------------
ProValue::ProValue(const QString &value, ProVariable *variable)
{
m_variable = variable;
m_value = value;
}
void ProValue::setValue(const QString &value)
{
m_value = value;
}
QString ProValue::value() const
QString ProVariable::value() const
{
return m_value;
}
void ProValue::setVariable(ProVariable *variable)
ProItem::ProItemKind ProVariable::kind() const
{
m_variable = variable;
return ProItem::VariableKind;
}
ProVariable *ProValue::variable() const
ProItem::ProItemReturn ProVariable::Accept(AbstractProItemVisitor *visitor)
{
return m_variable;
}
ProItem::ProItemKind ProValue::kind() const
{
return ProItem::ValueKind;
}
ProItem::ProItemReturn ProValue::Accept(AbstractProItemVisitor *visitor)
{
visitor->visitProValue(this);
visitor->visitProVariable(this);
return ReturnTrue;
}

View File

@@ -45,6 +45,7 @@ public:
FunctionKind,
ConditionKind,
OperatorKind,
VariableKind,
BlockKind
};
@@ -78,7 +79,6 @@ public:
NormalKind = 0x00,
ScopeKind = 0x01,
ScopeContentsKind = 0x02,
VariableKind = 0x04,
ProFileKind = 0x08,
FunctionBodyKind = 0x10,
SingleLine = 0x80
@@ -111,7 +111,7 @@ private:
int m_refCount;
};
class ProVariable : public ProBlock
class ProVariable : public ProItem
{
public:
enum VariableOperator {
@@ -122,7 +122,7 @@ public:
UniqueAddOperator = 4
};
ProVariable(const QString &name, ProBlock *parent);
ProVariable(const QString &name);
void setVariableOperator(VariableOperator variableKind);
VariableOperator variableOperator() const;
@@ -130,29 +130,16 @@ public:
void setVariable(const QString &name);
QString variable() const;
virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
private:
VariableOperator m_variableKind;
QString m_variable;
};
class ProValue : public ProItem
{
public:
ProValue(const QString &value, ProVariable *variable);
void setValue(const QString &value);
QString value() const;
void setVariable(ProVariable *variable);
ProVariable *variable() const;
ProItem::ProItemKind kind() const;
virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
private:
VariableOperator m_variableKind;
QString m_variable;
QString m_value;
ProVariable *m_variable;
};
class ProFunction : public ProItem

View File

@@ -41,10 +41,8 @@ void ProWriter::addFiles(ProFile *profile, QStringList *lines,
{
// Check if variable item exists as child of root item
foreach (ProItem *item, profile->items()) {
if (item->kind() == ProItem::BlockKind) {
ProBlock *block = static_cast<ProBlock *>(item);
if (block->blockKind() == ProBlock::VariableKind) {
ProVariable *proVar = static_cast<ProVariable*>(block);
if (item->kind() == ProItem::VariableKind) {
ProVariable *proVar = static_cast<ProVariable*>(item);
if (vars.contains(proVar->variable())
&& proVar->variableOperator() != ProVariable::RemoveOperator
&& proVar->variableOperator() != ProVariable::ReplaceOperator) {
@@ -78,7 +76,6 @@ void ProWriter::addFiles(ProFile *profile, QStringList *lines,
}
}
}
}
// Create & append new variable item
QString added = QLatin1Char('\n') + vars.first() + QLatin1String(" +=");
@@ -92,14 +89,11 @@ static void findProVariables(ProBlock *block, const QStringList &vars,
{
foreach (ProItem *item, block->items()) {
if (item->kind() == ProItem::BlockKind) {
ProBlock *subBlock = static_cast<ProBlock *>(item);
if (subBlock->blockKind() == ProBlock::VariableKind) {
ProVariable *proVar = static_cast<ProVariable*>(subBlock);
findProVariables(static_cast<ProBlock*>(item), vars, proVars);
} else if (item->kind() == ProItem::VariableKind) {
ProVariable *proVar = static_cast<ProVariable*>(item);
if (vars.contains(proVar->variable()))
*proVars << proVar;
} else {
findProVariables(subBlock, vars, proVars);
}
}
}
}