2017-01-03 10:14:32 +01:00
|
|
|
/***************************************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2016 Roman Telezhynskyi <dismine(at)gmail.com>
|
|
|
|
**
|
|
|
|
** Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
|
|
** software and associated documentation files (the "Software"), to deal in the Software
|
|
|
|
** without restriction, including without limitation the rights to use, copy, modify,
|
|
|
|
** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
** permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
|
|
**
|
|
|
|
** The above copyright notice and this permission notice shall be included in all copies or
|
|
|
|
** substantial portions of the Software.
|
|
|
|
**
|
|
|
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
|
|
|
** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
|
|
** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
**
|
|
|
|
******************************************************************************************************/
|
|
|
|
|
|
|
|
#include "qmudef.h"
|
|
|
|
|
|
|
|
#include <QLocale>
|
|
|
|
#include <QSet>
|
|
|
|
|
|
|
|
enum State
|
|
|
|
{
|
|
|
|
Init = 0,
|
|
|
|
Sign = 1,
|
|
|
|
Thousand = 2,
|
|
|
|
Mantissa = 3,
|
|
|
|
Dot = 4,
|
|
|
|
Abscissa = 5,
|
|
|
|
ExpMark = 6,
|
|
|
|
ExpSign = 7,
|
|
|
|
Exponent = 8,
|
|
|
|
Done = 9
|
|
|
|
};
|
|
|
|
|
|
|
|
enum InputToken
|
|
|
|
{
|
|
|
|
InputSign = 1,
|
|
|
|
InputThousand = 2,
|
|
|
|
InputDigit = 3,
|
|
|
|
InputDot = 4,
|
|
|
|
InputExp = 5
|
|
|
|
};
|
|
|
|
|
|
|
|
static const QChar QmuEOF = QChar(static_cast<ushort>(0xffff)); //guaranteed not to be a character.
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
static QChar GetChar(const QString &formula, int &index)
|
|
|
|
{
|
|
|
|
if (index >= formula.size())
|
|
|
|
{
|
|
|
|
return QmuEOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
return formula.at(index++);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
static QChar EatWhiteSpace(const QString &formula, int &index)
|
|
|
|
{
|
|
|
|
QChar c;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
c = GetChar(formula, index);
|
|
|
|
}
|
|
|
|
while ( c != QmuEOF && c.isSpace() );
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
static int CheckChar(QChar &c, const QLocale &locale, const QChar &decimal, const QChar &thousand)
|
|
|
|
{
|
|
|
|
INIT_LOCALE_VARIABLES(locale);
|
|
|
|
|
|
|
|
if (c == positiveSign)
|
|
|
|
{
|
|
|
|
c = '+';
|
|
|
|
return InputToken::InputSign;
|
|
|
|
}
|
|
|
|
else if (c == negativeSign)
|
|
|
|
{
|
|
|
|
c = '-';
|
|
|
|
return InputToken::InputSign;
|
|
|
|
}
|
|
|
|
else if (c == sign0)
|
|
|
|
{
|
|
|
|
c = '0';
|
|
|
|
return InputToken::InputDigit;
|
|
|
|
}
|
|
|
|
else if (c == sign1)
|
|
|
|
{
|
|
|
|
c = '1';
|
|
|
|
return InputToken::InputDigit;
|
|
|
|
}
|
|
|
|
else if (c == sign2)
|
|
|
|
{
|
|
|
|
c = '2';
|
|
|
|
return InputToken::InputDigit;
|
|
|
|
}
|
|
|
|
else if (c == sign3)
|
|
|
|
{
|
|
|
|
c = '3';
|
|
|
|
return InputToken::InputDigit;
|
|
|
|
}
|
|
|
|
else if (c == sign4)
|
|
|
|
{
|
|
|
|
c = '4';
|
|
|
|
return InputToken::InputDigit;
|
|
|
|
}
|
|
|
|
else if (c == sign5)
|
|
|
|
{
|
|
|
|
c = '5';
|
|
|
|
return InputToken::InputDigit;
|
|
|
|
}
|
|
|
|
else if (c == sign6)
|
|
|
|
{
|
|
|
|
c = '6';
|
|
|
|
return InputToken::InputDigit;
|
|
|
|
}
|
|
|
|
else if (c == sign7)
|
|
|
|
{
|
|
|
|
c = '7';
|
|
|
|
return InputToken::InputDigit;
|
|
|
|
}
|
|
|
|
else if (c == sign8)
|
|
|
|
{
|
|
|
|
c = '8';
|
|
|
|
return InputToken::InputDigit;
|
|
|
|
}
|
|
|
|
else if (c == sign9)
|
|
|
|
{
|
|
|
|
c = '9';
|
|
|
|
return InputToken::InputDigit;
|
|
|
|
}
|
|
|
|
else if (c == decimal)
|
|
|
|
{
|
|
|
|
return InputToken::InputDot;
|
|
|
|
}
|
|
|
|
else if (c == thousand)
|
|
|
|
{
|
|
|
|
return InputToken::InputThousand;
|
|
|
|
}
|
|
|
|
else if (c == expLower)
|
|
|
|
{
|
|
|
|
c = 'e';
|
|
|
|
return InputToken::InputExp;
|
|
|
|
}
|
|
|
|
else if (c == expUpper)
|
|
|
|
{
|
|
|
|
c = 'E';
|
|
|
|
return InputToken::InputExp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
int ReadVal(const QString &formula, qreal &val, const QLocale &locale, const QChar &decimal, const QChar &thousand)
|
|
|
|
{
|
|
|
|
// Must not be equal
|
|
|
|
if (decimal == thousand || formula.isEmpty())
|
|
|
|
{
|
|
|
|
val = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
INIT_LOCALE_VARIABLES(locale);
|
|
|
|
|
|
|
|
QSet<QChar> reserved;
|
|
|
|
reserved << positiveSign
|
|
|
|
<< negativeSign
|
|
|
|
<< sign0
|
|
|
|
<< sign1
|
|
|
|
<< sign2
|
|
|
|
<< sign3
|
|
|
|
<< sign4
|
|
|
|
<< sign5
|
|
|
|
<< sign6
|
|
|
|
<< sign7
|
|
|
|
<< sign8
|
|
|
|
<< sign9
|
|
|
|
<< expUpper
|
|
|
|
<< expLower;
|
|
|
|
|
|
|
|
if (reserved.contains(decimal) || reserved.contains(thousand))
|
|
|
|
{
|
|
|
|
val = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// row - current state, column - new state
|
|
|
|
static uchar table[9][6] =
|
|
|
|
{
|
|
|
|
/* None InputSign InputThousand InputDigit InputDot InputExp */
|
|
|
|
{ 0, State::Sign, 0, State::Mantissa, State::Dot, 0, }, // Init
|
|
|
|
{ 0, 0, 0, State::Mantissa, State::Dot, 0, }, // Sign
|
|
|
|
{ 0, 0, 0, State::Mantissa, 0, 0, }, // Thousand
|
|
|
|
{ State::Done, State::Done, State::Thousand, State::Mantissa, State::Dot, State::ExpMark,}, // Mantissa
|
|
|
|
{ 0, 0, 0, State::Abscissa, 0, 0, }, // Dot
|
|
|
|
{ State::Done, State::Done, 0, State::Abscissa, 0, State::ExpMark,}, // Abscissa
|
|
|
|
{ 0, State::ExpSign, 0, State::Exponent, 0, 0, }, // ExpMark
|
|
|
|
{ 0, 0, 0, State::Exponent, 0, 0, }, // ExpSign
|
|
|
|
{ State::Done, 0, 0, State::Exponent, 0, State::Done } // Exponent
|
|
|
|
};
|
|
|
|
|
|
|
|
int state = State::Init; // parse state
|
|
|
|
QString buf;
|
|
|
|
|
|
|
|
int index = 0; // start position
|
|
|
|
QChar c = EatWhiteSpace(formula, index);
|
|
|
|
|
|
|
|
while ( true )
|
|
|
|
{
|
2017-01-03 10:32:19 +01:00
|
|
|
const int input = CheckChar(c, locale, decimal, thousand);// input token
|
2017-01-03 10:14:32 +01:00
|
|
|
|
|
|
|
state = table[state][input];
|
|
|
|
|
|
|
|
if (state == 0)
|
|
|
|
{
|
|
|
|
val = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (state == Done)
|
|
|
|
{
|
|
|
|
// Convert to C locale
|
|
|
|
QLocale cLocale(QLocale::C);
|
|
|
|
const QChar cDecimal = cLocale.decimalPoint();
|
|
|
|
const QChar cThousand = cLocale.groupSeparator();
|
|
|
|
if (locale != cLocale && (cDecimal != decimal || cThousand != thousand))
|
|
|
|
{
|
|
|
|
if (decimal == cThousand)
|
|
|
|
{// Handle reverse to C locale case: thousand '.', decimal ','
|
|
|
|
const QChar tmpThousand = '@';
|
|
|
|
buf.replace(thousand, tmpThousand);
|
|
|
|
buf.replace(decimal, cDecimal);
|
|
|
|
buf.replace(tmpThousand, cThousand);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buf.replace(thousand, cThousand);
|
|
|
|
buf.replace(decimal, cDecimal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ok = false;
|
|
|
|
const double d = cLocale.toDouble(buf, &ok);
|
|
|
|
if (ok)
|
|
|
|
{
|
|
|
|
val = d;
|
|
|
|
return buf.size();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
val = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.append(c);
|
|
|
|
c = GetChar(formula, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|