You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdewebdev/kommander/widget/expression.cpp

333 lines
7.9 KiB

/***************************************************************************
expression.cpp - Expression parser
-------------------
copyright : (C) 2004 Michal Rudolf <mrudolf@kdewebdwev.org>
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "expression.h"
#include <klocale.h>
Expression::Expression() : m_start(0), m_error(false)
{
}
Expression::Expression(const TQString& expr)
{
*this = expr;
}
Expression& Expression::operator=(const TQString& s)
{
m_start = 0;
m_error = false;
m_parts.clear();
const TQString single = "()<>!+-/*%";
int start = 0;
int len = s.length();
int i = 0;
while (i < len)
{
if (((s[i] == '>' || s[i] == '<' || s[i] == '=' || s[i] == '!') &&
s[i + 1] == '=') || (s[i] == '<' && s[i + 1] == '>'))
{
m_parts.append(TQVariant(s.mid(i, 2)));
i += 2;
} else if (s[i].isDigit())
{
i++;
bool decimal = false;
while (i < len && (s[i].isDigit() || (!decimal && s[i] == TQChar('.'))))
{
if (s[i] == '.')
decimal = true;
i++;
}
if (decimal)
m_parts.append(TQVariant(s.mid(start, i - start).toDouble()));
else
m_parts.append(TQVariant(s.mid(start, i - start).toInt()));
} else if (single.contains(s[i]))
m_parts.append(TQVariant(TQString(s[i++])));
else if (s[i] == '\"')
{
i++;
while (i < len && s[i] != '\"')
i++;
m_parts.append(TQVariant(s.mid(start + 1, i - start - 1)));
i++;
} else if (s[i].isSpace())
while (i < len && s[i].isSpace())
i++;
else
{
while (i < len && !s[i].isSpace())
i++;
TQString keyword = s.mid(start, i - start);
if (keyword == "true")
m_parts.append(TQVariant(true));
else if (keyword == "false")
m_parts.append(TQVariant(false));
else /* will be deprecated soon */
m_parts.append(TQVariant(keyword));
}
start = i;
}
return *this;
}
TQString Expression::next() const
{
if (m_start < m_parts.count())
return m_parts[m_start].toString();
else
return TQString();
}
bool Expression::validate()
{
if (m_start >= m_parts.count())
setError();
return !m_error;
}
Expression::Type Expression::commonType(const TQVariant v1, const TQVariant v2) const
{
if (v1.type() == TQVariant::String || v2.type() == TQVariant::String)
return TypeString;
else if (v1.type() == TQVariant::Double || v2.type() == TQVariant::Double)
return TypeDouble;
return TypeInt;
}
static int expression_compareDouble(const double A, const double B)
{
return A<B ? -1 : (A==B ? 0 : 1);
}
int Expression::compare(const TQVariant v1, const TQVariant v2) const
{
switch (commonType(v1, v2)) {
case TypeString: return v1.toString().compare(v2.toString());
case TypeDouble: return expression_compareDouble(v1.toDouble(), v2.toDouble());
case TypeInt: return v1.toInt() - v2.toInt();
default: return 0;
}
}
void Expression::setError(int pos)
{
m_errorPosition = pos == -1 ? m_start : pos;
m_error = true;
}
TQVariant Expression::parseNumber()
{
if (!validate())
return -1;
return m_parts[m_start++];
}
TQVariant Expression::parseMinus()
{
if (!validate()) return -1;
bool sign = next() == "-";
if (sign)
{
m_start++;
TQVariant value = parseNumber();
if (value.type() == TQVariant::Double)
return -value.toDouble();
else
return -value.toInt();
}
else
return parseNumber();
}
TQVariant Expression::parseBracket()
{
if (!validate()) return -1;
if (next() == "(")
{
m_start++;
TQVariant value = parse();
if (next() == ")")
m_start++;
else
setError();
return value;
}
else
return parseMinus();
}
TQVariant Expression::parseMultiply()
{
if (!validate()) return -1;
TQVariant value = parseBracket();
TQString op = next();
while (op == "*" || op == "/" || op == "%")
{
m_start++;
TQVariant value2 = parseBracket();
Type mode = commonType(value, value2);
if (op == "*")
{
if (mode == TypeDouble)
value = value.toDouble() * value2.toDouble();
else
value = value.toInt() * value2.toInt();
}
else if (op == "/")
{
if (value2.toInt() == 0)
return i18n("error");
if (mode == TypeDouble || value.toInt() != value.toInt() / value2.toInt() * value2.toInt())
value = value.toDouble() / value2.toDouble();
else
value = value.toInt() / value2.toInt();
}
else
{
if (value2.toInt() == 0)
return i18n("error");
if (mode == TypeDouble)
value = value.toDouble() / value2.toInt();
else
value = value.toInt() / value2.toInt();
}
op = next();
}
return value;
}
TQVariant Expression::parseAdd()
{
if (!validate()) return -1;
TQVariant value = parseMultiply();
TQString op = next();
while (op == "+" || op == "-")
{
m_start++;
TQVariant value2 = parseMultiply();
Type mode = commonType(value, value2);
if (op == "+")
if (mode == TypeDouble)
value = value.toDouble() + value2.toDouble();
else
value = value.toInt() + value2.toInt();
else
if (mode == TypeDouble)
value = value.toDouble() - value2.toDouble();
else
value = value.toInt() - value2.toInt();
op = next();
}
return value;
}
TQVariant Expression::parseComparison()
{
if (!validate()) return -1;
TQVariant value = parseAdd();
TQString cmp = next();
if (cmp == "<" || cmp == "<=" || cmp == "==" || cmp == ">=" || cmp == ">" || cmp == "<>" || cmp == "!=")
{
m_start++;
TQVariant value2 = parseAdd();
if (cmp == "<")
return compare(value, value2) < 0;
else if (cmp == "<=")
return compare(value, value2) <= 0;
else if (cmp == "==")
return compare(value, value2) == 0;
else if (cmp == ">=")
return compare(value, value2) >= 0;
else if (cmp == "<>" || cmp == "!=")
return compare(value, value2) != 0;
else
return compare(value, value2) > 0;
}
return value;
}
TQVariant Expression::parseNot()
{
if (next() == "!" || next() == "not")
{
m_start++;
return !parseComparison().asBool();
}
else
return parseComparison();
}
TQVariant Expression::parseAnd()
{
if (!validate()) return -1;
TQVariant value = parseNot();
while (next() == "&&" || next() == "and")
{
m_start++;
value = parseNot().toBool() && value.toBool();
}
return value;
}
TQVariant Expression::parseOr()
{
if (!validate()) return -1;
TQVariant value = parseAnd();
while (next() == "||" || next() == "or")
{
m_start++;
value = parseAnd().toBool() || value.toBool();
}
return value;
}
TQVariant Expression::parse()
{
return parseOr();
}
TQVariant Expression::value(bool* valid)
{
m_start = 0;
m_error = false;
TQVariant val = parse();
if (valid)
*valid = !m_error && m_start == m_parts.count();
return val;
}
TQVariant Expression::value(const TQString& s, bool* valid)
{
*this = s;
return value(valid);
}
bool Expression::isTrue(const TQString& s, bool* valid)
{
TQVariant v = value(s, valid);
return (v.type() == TQVariant::String && !v.toString().isNull()) ||
(v.type() != TQVariant::String && v.toInt() != 0);
}