src/hbcore/cssparser/hbexpressionparser_p.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 04 Oct 2010 00:38:12 +0300
changeset 30 80e4d18b72f5
permissions -rw-r--r--
Revision: 201037 Kit: 201039

/****************************************************************************
**
** 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;
}