/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbCore module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/
#include "hbexpressionparser_p.h"
#include "hbhash_p.h"
#include <QStack>
static bool parseExpressionOperand(
const QString &str,
HbExpressionParser::Token &type,
int &value)
{
QString valueString = str;
if (valueString.startsWith("var(") && valueString.endsWith(")")) {
// remove var( and last )
type = HbExpressionParser::Variable;
value = hbHash(valueString.midRef(4, valueString.size()-5));
} else {
if (valueString.endsWith(QLatin1String("un"), Qt::CaseInsensitive)) {
valueString.chop(2);
type = HbExpressionParser::LengthInUnits;
} else if (valueString.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
valueString.chop(2);
type = HbExpressionParser::LengthInPixels;
} else if (valueString.endsWith(QLatin1String("mm"), Qt::CaseInsensitive)) {
valueString.chop(2);
type = HbExpressionParser::LengthInMillimeters;
} else {
// assume pixels
type = HbExpressionParser::LengthInPixels;
}
bool ok(true);
value = HbExpressionParser::toFixed(valueString.toFloat(&ok));
if (!ok) {
return false;
}
}
return true;
}
static int precedence(HbExpressionParser::Token op)
{
switch (op) {
case HbExpressionParser::Addition:
case HbExpressionParser::Subtraction:
return 1;
case HbExpressionParser::Multiplication:
case HbExpressionParser::Division:
return 2;
case HbExpressionParser::Negation:
return 3;
default:
return 0;
}
}
static bool isLeft(HbExpressionParser::Token op)
{
switch (op) {
case HbExpressionParser::Addition:
case HbExpressionParser::Subtraction:
case HbExpressionParser::Multiplication:
case HbExpressionParser::Division:
return true;
case HbExpressionParser::Negation:
default:
return false;
}
}
bool HbExpressionParser::parse(const QString &str, QList<int> &tokens)
{
tokens.clear();
// Parse the infix notation to "tempTokens"
QList<int> tempTokens;
const QChar SPACE(' ');
const QChar OPENPARENTHESIS('(');
const QChar CLOSEPARENTHESIS(')');
const QChar PLUS('+');
const QChar MINUS('-');
const QChar STAR('*');
const QChar SLASH('/');
int position = 0;
if (str.startsWith("expr(") && str.endsWith(")")) {
position = 4;
} else if (str.startsWith("-expr(") && str.endsWith(")")) {
position = 5;
tempTokens.append(LengthInPixels);
tempTokens.append(toFixed(-1.0));
tempTokens.append(Multiplication);
}
int begin = -1;
int nestingLevel = 0;
bool parseVariable = false;
bool endMark = false;
int operatorCount = 1;//there can only be 2 sequental operators if the latter one is negation
while (position < str.size()) {
endMark = false;
bool appendCloseParenthesis(false);
if (str.at(position) == SPACE) {
endMark = true;
} else if ((str.at(position) == OPENPARENTHESIS) && !parseVariable) {
tempTokens.append(OpenParenthesis);
nestingLevel++;
position++;
operatorCount = 1;
continue;
} else if (str.at(position) == CLOSEPARENTHESIS) {
if (parseVariable) {
parseVariable = false;
position++;
continue;
}
appendCloseParenthesis = true;
nestingLevel--;
endMark = true;
} else if ((str.at(position) == MINUS) && !parseVariable) {
endMark = true;
} else if ((str.at(position) == PLUS) ||
(str.at(position) == STAR) ||
(str.at(position) == SLASH)) {
endMark = true;
}
if (endMark) {
if (begin >= 0) {
// parse operand
QString valueString = str.mid(begin, position - begin);
if (operatorCount==0) {
// an operand without operator
return false;
}
HbExpressionParser::Token type;
int val;
if ( !parseExpressionOperand(valueString, type, val ) ) {
return false;
}
tempTokens.append(type);
tempTokens.append(val);
operatorCount = 0;
}
begin = -1;
if (str.at(position) == PLUS) {
if (operatorCount > 0) {
return false;
}
tempTokens.append(Addition);
operatorCount++;
} else if (str.at(position) == MINUS) {
if (operatorCount == 1) {
tempTokens.append(Negation);
} else if (operatorCount > 1) {
return false;
} else {
tempTokens.append(Subtraction);
}
operatorCount++;
} else if (str.at(position) == STAR) {
if (operatorCount > 0) {
return false;
}
tempTokens.append(Multiplication);
operatorCount++;
} else if (str.at(position) == SLASH) {
if (operatorCount > 0) {
return false;
}
tempTokens.append(Division);
operatorCount++;
}
if (appendCloseParenthesis) {
tempTokens.append(CloseParenthesis);
if (operatorCount != 0) {
// an operator without operand
return false;
}
}
position++;
continue;
}
if (begin == -1) {
begin = position;
}
// flag variable parsing (variable syntax contains parenthesis)
if ((str.at(position) == QChar('v')) && !parseVariable) {
parseVariable = true;
position++;
continue;
}
position++;
}
// check for unmatching parentheses
if (nestingLevel != 0) {
return false;
}
// parse last value
if (begin >= 0) {
// parse operand
if (operatorCount==0) {
// an operand without operator
return false;
}
QString valueString = str.mid(begin, position - begin);
HbExpressionParser::Token type;
int val;
if ( !parseExpressionOperand(valueString, type, val ) ) {
return false;
}
tempTokens.append(type);
tempTokens.append(val);
} else {
if (operatorCount!=0) {
// an operator without operand
return false;
}
}
if (tempTokens.count()<2) {
return false;
}
// Convert the infix notation to RPN with "shunting-yard" algorithm
QStack<Token> operatorStack;
for (int i=0; i<tempTokens.count(); i++) {
Token t = (Token)tempTokens.at(i);
switch (t) {
case Variable:
case LengthInPixels:
case LengthInUnits:
case LengthInMillimeters:
tokens.append(tempTokens.at(i));
i++;
tokens.append(tempTokens.at(i));
break;
case OpenParenthesis:
operatorStack.push(t);
break;
case CloseParenthesis:
while (operatorStack.count()) {
Token op = operatorStack.pop();
if (op == OpenParenthesis) {
break;
} else {
tokens.append(op);
}
}
break;
case Addition:
case Subtraction:
case Multiplication:
case Division:
case Negation:
while (operatorStack.count()) {
Token op = operatorStack.top();
if ((isLeft(t) && (precedence(t) <= precedence(op))) ||
(!isLeft(t) && (precedence(t) < precedence(op)))) {
tokens.append(op);
operatorStack.pop();
} else {
break;
}
}
operatorStack.push(t);
break;
default:
break;
}
}
while (operatorStack.count()) {
tokens.append(operatorStack.pop());
}
return true;
}
int HbExpressionParser::toFixed(float f)
{
Q_ASSERT( sizeof(quint32) == sizeof(float) );
quint32 u;
memcpy(&u, &f, sizeof(quint32));
return u;
}
int HbExpressionParser::toFixed(double d)
{
return toFixed((float)d);
}
qreal HbExpressionParser::fromFixed(int i)
{
float f;
memcpy(&f, &i, sizeof(float));
return f;
}