Resolved issue #606. Mac OS X. Can’t type in measurements due to digit count
limitation. --HG-- branch : develop
This commit is contained in:
parent
290f78651d
commit
da6cd92b22
|
@ -48,6 +48,7 @@
|
|||
- [#582] Issue with standard path to shared data on Linux.
|
||||
- [#595] GapWidth affecting to the margins.
|
||||
- [#589] Valentina lock up if not enough space for label.
|
||||
- [#606] Mac OS X. Can’t type in measurements due to digit count limitation.
|
||||
|
||||
# Version 0.4.6
|
||||
- [#594] Broken export on Mac.
|
||||
|
|
275
src/libs/qmuparser/qmudef.cpp
Normal file
275
src/libs/qmuparser/qmudef.cpp
Normal file
|
@ -0,0 +1,275 @@
|
|||
/***************************************************************************************************
|
||||
**
|
||||
** 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
|
||||
int input; // input token
|
||||
QString buf;
|
||||
|
||||
int index = 0; // start position
|
||||
QChar c = EatWhiteSpace(formula, index);
|
||||
|
||||
while ( true )
|
||||
{
|
||||
input = CheckChar(c, locale, decimal, thousand);
|
||||
|
||||
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;
|
||||
}
|
|
@ -90,6 +90,25 @@ QT_WARNING_DISABLE_GCC("-Wattributes")
|
|||
#ifdef Q_CC_MSVC
|
||||
#include <ciso646>
|
||||
#endif /* Q_CC_MSVC */
|
||||
|
||||
class QLocale;
|
||||
|
||||
#define INIT_LOCALE_VARIABLES(locale) \
|
||||
const QChar positiveSign = (locale).positiveSign(); \
|
||||
const QChar negativeSign = (locale).negativeSign(); \
|
||||
const QChar sign0 = (locale).toString(0).at(0); \
|
||||
const QChar sign1 = (locale).toString(1).at(0); \
|
||||
const QChar sign2 = (locale).toString(2).at(0); \
|
||||
const QChar sign3 = (locale).toString(3).at(0); \
|
||||
const QChar sign4 = (locale).toString(4).at(0); \
|
||||
const QChar sign5 = (locale).toString(5).at(0); \
|
||||
const QChar sign6 = (locale).toString(6).at(0); \
|
||||
const QChar sign7 = (locale).toString(7).at(0); \
|
||||
const QChar sign8 = (locale).toString(8).at(0); \
|
||||
const QChar sign9 = (locale).toString(9).at(0); \
|
||||
const QChar expUpper = (locale).exponential().toUpper(); \
|
||||
const QChar expLower = (locale).exponential().toLower() \
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
inline QString NameRegExp()
|
||||
{
|
||||
|
@ -119,4 +138,6 @@ static inline bool QmuFuzzyComparePossibleNulls(double p1, double p2)
|
|||
}
|
||||
}
|
||||
|
||||
int ReadVal(const QString &formula, qreal &val, const QLocale &locale, const QChar &decimal, const QChar &thousand);
|
||||
|
||||
#endif // QMUDEF_H
|
||||
|
|
|
@ -46,13 +46,13 @@ QmuFormulaBase::~QmuFormulaBase()
|
|||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief InitCharacterSets init character set for parser.
|
||||
* @brief InitCharSets init character set for parser.
|
||||
*
|
||||
* QMuParser require setting character set for legal characters. Because we try make our expresion language independent
|
||||
* we set all posible unique characters from all alphabets.
|
||||
*
|
||||
*/
|
||||
void QmuFormulaBase::InitCharacterSets()
|
||||
void QmuFormulaBase::InitCharSets()
|
||||
{
|
||||
//String with all unique symbols for supported alphabets.
|
||||
//See script alphabets.py for generation and more information.
|
||||
|
@ -68,9 +68,18 @@ void QmuFormulaBase::InitCharacterSets()
|
|||
<< "ЪƯخγвŅԴŪضλкԼĴσтÅՄنъÍՌRӕՔZÝŜbåդﻩjíլļrӵմzýռپêЅքćچЍďӱҒЕůėژșΘØҚНğńءΠFҢХħΨҪ"
|
||||
<< "ЭųįҶرҲеԷňعθҺнԿفπÂхՇψÊэšՏÒUəÚѝŻşҤӑâeէŐımկòuշÕúտŔ";
|
||||
|
||||
INIT_LOCALE_VARIABLES(m_locale);
|
||||
|
||||
// Defining identifier character sets
|
||||
DefineNameChars(QStringLiteral("0123456789_@#'") + symbols.join(""));
|
||||
DefineOprtChars(symbols.join("") + QStringLiteral("+-*^/?<>=!$%&|~_"));
|
||||
const QString nameChars = QString() + sign0 + sign1 + sign2 + sign3 + sign4 + sign5 + sign6 + sign7 + sign8 +
|
||||
sign9 + QLatin1String("_@#'") + symbols.join("");
|
||||
DefineNameChars(nameChars);
|
||||
|
||||
const QString oprtChars = symbols.join("") + positiveSign + negativeSign + QLatin1String("*^/?<>=!$%&|~'_");
|
||||
DefineOprtChars(oprtChars);
|
||||
|
||||
const QString infixOprtChars = QString() + positiveSign + negativeSign + QLatin1String("*^/?<>=!$%&|~'_");
|
||||
DefineInfixOprtChars(infixOprtChars);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -94,12 +103,13 @@ void QmuFormulaBase::SetSepForTr(bool osSeparator, bool fromUser)
|
|||
{
|
||||
if (fromUser)
|
||||
{
|
||||
const QLocale loc = QLocale();
|
||||
setLocale(loc);
|
||||
SetArgSep(';');
|
||||
if (osSeparator)
|
||||
{
|
||||
const QLocale loc = QLocale::system();
|
||||
SetDecSep(loc.decimalPoint().toLatin1());
|
||||
SetThousandsSep(loc.groupSeparator().toLatin1());
|
||||
SetArgSep(';');
|
||||
setDecimalPoint(loc.decimalPoint());
|
||||
setThousandsSeparator(loc.groupSeparator());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -114,8 +124,8 @@ void QmuFormulaBase::SetSepForTr(bool osSeparator, bool fromUser)
|
|||
void QmuFormulaBase::SetSepForEval()
|
||||
{
|
||||
SetArgSep(';');
|
||||
SetThousandsSep(',');
|
||||
SetDecSep('.');
|
||||
setThousandsSeparator(',');
|
||||
setDecimalPoint('.');
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -41,14 +41,14 @@ public:
|
|||
QmuFormulaBase();
|
||||
virtual ~QmuFormulaBase() Q_DECL_OVERRIDE;
|
||||
|
||||
virtual void InitCharSets() Q_DECL_OVERRIDE;
|
||||
|
||||
static void RemoveAll(QMap<int, QString> &map, const QString &val);
|
||||
|
||||
protected:
|
||||
void InitCharacterSets();
|
||||
static qreal* AddVariable(const QString &a_szName, void *a_pUserData);
|
||||
void SetSepForTr(bool osSeparator, bool fromUser);
|
||||
void SetSepForEval();
|
||||
|
||||
static void RemoveAll(QMap<int, QString> &map, const QString &val);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QmuFormulaBase)
|
||||
};
|
||||
|
|
|
@ -225,23 +225,19 @@ qreal QmuParser::Max(const qreal *a_afArg, int a_iArgc)
|
|||
* @param [out] a_fVal Pointer where the value should be stored in case one is found.
|
||||
* @return 1 if a value was found 0 otherwise.
|
||||
*/
|
||||
int QmuParser::IsVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const std::locale &s_locale)
|
||||
int QmuParser::IsVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const QLocale &locale, const QChar &decimal,
|
||||
const QChar &thousand)
|
||||
{
|
||||
qreal fVal(0);
|
||||
|
||||
std::wstring a_szExprStd = a_szExpr.toStdWString();
|
||||
stringstream_type stream(a_szExprStd);
|
||||
stream.seekg(0); // todo: check if this really is necessary
|
||||
stream.imbue(s_locale);
|
||||
stream >> fVal;
|
||||
stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading
|
||||
const int pos = ReadVal(a_szExpr, fVal, locale, decimal, thousand);
|
||||
|
||||
if (iEnd==static_cast<stringstream_type::pos_type>(-1))
|
||||
if (pos == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
*a_iPos += static_cast<int>(iEnd);
|
||||
*a_iPos += pos;
|
||||
*a_fVal = fVal;
|
||||
return 1;
|
||||
}
|
||||
|
@ -340,7 +336,7 @@ void QmuParser::InitConst()
|
|||
*/
|
||||
void QmuParser::InitOprt()
|
||||
{
|
||||
DefineInfixOprt("-", UnaryMinus);
|
||||
DefineInfixOprt(m_locale.negativeSign(), UnaryMinus);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <QtGlobal>
|
||||
#include <locale>
|
||||
|
||||
#include "../qmuparser/qmuparser_global.h"
|
||||
#include "qmuparser_global.h"
|
||||
#include "qmuparserbase.h"
|
||||
|
||||
|
@ -59,7 +58,8 @@ namespace qmu
|
|||
virtual void OnDetectVar(const QString &pExpr, int &nStart, int &nEnd) Q_DECL_OVERRIDE;
|
||||
qreal Diff(qreal *a_Var, qreal a_fPos, qreal a_fEpsilon = 0) const;
|
||||
protected:
|
||||
static int IsVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const std::locale &s_locale);
|
||||
static int IsVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const QLocale &locale,
|
||||
const QChar &decimal, const QChar &thousand);
|
||||
// hyperbolic functions
|
||||
static qreal Sinh(qreal);
|
||||
static qreal Cosh(qreal);
|
||||
|
|
|
@ -11,7 +11,8 @@ SOURCES += \
|
|||
$$PWD/qmuparsertest.cpp \
|
||||
$$PWD/qmutranslation.cpp \
|
||||
$$PWD/qmuformulabase.cpp \
|
||||
$$PWD/qmutokenparser.cpp
|
||||
$$PWD/qmutokenparser.cpp \
|
||||
$$PWD/qmudef.cpp
|
||||
|
||||
win32-msvc*:SOURCES += $$PWD/stable.cpp
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ OBJECTS_DIR = obj
|
|||
|
||||
include(qmuparser.pri)
|
||||
|
||||
VERSION = 2.4.1
|
||||
VERSION = 2.5.0
|
||||
|
||||
# Allow MAC OS X to find library inside a bundle
|
||||
macx:QMAKE_SONAME_PREFIX = @rpath
|
||||
|
|
|
@ -62,11 +62,31 @@ const QStringList QmuParserBase::c_DefaultOprt = QStringList()<< "<=" << ">=" <<
|
|||
* @brief Constructor.
|
||||
*/
|
||||
QmuParserBase::QmuParserBase()
|
||||
:s_locale(std::locale(std::locale::classic(), new change_dec_sep<char_type>('.'))),
|
||||
m_pParseFormula(&QmuParserBase::ParseString), m_vRPN(), m_vStringBuf(), m_vStringVarBuf(), m_pTokenReader(),
|
||||
m_FunDef(), m_PostOprtDef(), m_InfixOprtDef(), m_OprtDef(), m_ConstDef(), m_StrVarDef(), m_VarDef(),
|
||||
m_bBuiltInOp(true), m_sNameChars(), m_sOprtChars(), m_sInfixOprtChars(), m_nIfElseCounter(0), m_vStackBuffer(),
|
||||
m_nFinalResultIdx(0), m_Tokens(QMap<int, QString>()), m_Numbers(QMap<int, QString>()), allowSubexpressions(true)
|
||||
: m_locale(QLocale::c()),
|
||||
m_decimalPoint(QLocale::c().decimalPoint()),
|
||||
m_thousandsSeparator(QLocale::c().groupSeparator()),
|
||||
m_pParseFormula(&QmuParserBase::ParseString),
|
||||
m_vRPN(),
|
||||
m_vStringBuf(),
|
||||
m_vStringVarBuf(),
|
||||
m_pTokenReader(),
|
||||
m_FunDef(),
|
||||
m_PostOprtDef(),
|
||||
m_InfixOprtDef(),
|
||||
m_OprtDef(),
|
||||
m_ConstDef(),
|
||||
m_StrVarDef(),
|
||||
m_VarDef(),
|
||||
m_bBuiltInOp(true),
|
||||
m_sNameChars(),
|
||||
m_sOprtChars(),
|
||||
m_sInfixOprtChars(),
|
||||
m_nIfElseCounter(0),
|
||||
m_vStackBuffer(),
|
||||
m_nFinalResultIdx(0),
|
||||
m_Tokens(QMap<int, QString>()),
|
||||
m_Numbers(QMap<int, QString>()),
|
||||
allowSubexpressions(true)
|
||||
{
|
||||
InitTokenReader();
|
||||
}
|
||||
|
@ -78,11 +98,31 @@ QmuParserBase::QmuParserBase()
|
|||
* Tha parser can be safely copy constructed but the bytecode is reset during copy construction.
|
||||
*/
|
||||
QmuParserBase::QmuParserBase(const QmuParserBase &a_Parser)
|
||||
:s_locale(a_Parser.getLocale()), m_pParseFormula(&QmuParserBase::ParseString), m_vRPN(), m_vStringBuf(),
|
||||
m_vStringVarBuf(), m_pTokenReader(), m_FunDef(), m_PostOprtDef(), m_InfixOprtDef(), m_OprtDef(), m_ConstDef(),
|
||||
m_StrVarDef(), m_VarDef(), m_bBuiltInOp(true), m_sNameChars(), m_sOprtChars(), m_sInfixOprtChars(),
|
||||
m_nIfElseCounter(0), m_vStackBuffer(), m_nFinalResultIdx(0), m_Tokens(QMap<int, QString>()),
|
||||
m_Numbers(QMap<int, QString>()), allowSubexpressions(true)
|
||||
: m_locale(a_Parser.getLocale()),
|
||||
m_decimalPoint(a_Parser.getDecimalPoint()),
|
||||
m_thousandsSeparator(a_Parser.getThousandsSeparator()),
|
||||
m_pParseFormula(&QmuParserBase::ParseString),
|
||||
m_vRPN(),
|
||||
m_vStringBuf(),
|
||||
m_vStringVarBuf(),
|
||||
m_pTokenReader(),
|
||||
m_FunDef(),
|
||||
m_PostOprtDef(),
|
||||
m_InfixOprtDef(),
|
||||
m_OprtDef(),
|
||||
m_ConstDef(),
|
||||
m_StrVarDef(),
|
||||
m_VarDef(),
|
||||
m_bBuiltInOp(true),
|
||||
m_sNameChars(),
|
||||
m_sOprtChars(),
|
||||
m_sInfixOprtChars(),
|
||||
m_nIfElseCounter(0),
|
||||
m_vStackBuffer(),
|
||||
m_nFinalResultIdx(0),
|
||||
m_Tokens(QMap<int, QString>()),
|
||||
m_Numbers(QMap<int, QString>()),
|
||||
allowSubexpressions(true)
|
||||
{
|
||||
m_pTokenReader.reset(new token_reader_type(this));
|
||||
Assign(a_Parser);
|
||||
|
@ -150,38 +190,6 @@ void QmuParserBase::Assign(const QmuParserBase &a_Parser)
|
|||
m_sInfixOprtChars = a_Parser.m_sInfixOprtChars;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief Set the decimal separator.
|
||||
* @param cDecSep Decimal separator as a character value.
|
||||
* @sa SetThousandsSep
|
||||
*
|
||||
* By default muparser uses the "C" locale. The decimal separator of this
|
||||
* locale is overwritten by the one provided here.
|
||||
*/
|
||||
// cppcheck-suppress unusedFunction
|
||||
void QmuParserBase::SetDecSep(char_type cDecSep)
|
||||
{
|
||||
char_type cThousandsSep = std::use_facet< change_dec_sep<char_type> >(s_locale).thousands_sep();
|
||||
s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>(cDecSep, cThousandsSep));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief Sets the thousands operator.
|
||||
* @param cThousandsSep The thousands separator as a character
|
||||
* @sa SetDecSep
|
||||
*
|
||||
* By default muparser uses the "C" locale. The thousands separator of this
|
||||
* locale is overwritten by the one provided here.
|
||||
*/
|
||||
// cppcheck-suppress unusedFunction
|
||||
void QmuParserBase::SetThousandsSep(char_type cThousandsSep)
|
||||
{
|
||||
char_type cDecSep = std::use_facet< change_dec_sep<char_type> >(s_locale).decimal_point();
|
||||
s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>(cDecSep, cThousandsSep));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief Resets the locale.
|
||||
|
@ -191,8 +199,10 @@ void QmuParserBase::SetThousandsSep(char_type cThousandsSep)
|
|||
// cppcheck-suppress unusedFunction
|
||||
void QmuParserBase::ResetLocale()
|
||||
{
|
||||
s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>('.'));
|
||||
SetArgSep(',');
|
||||
setLocale(QLocale::c());
|
||||
m_decimalPoint = m_locale.decimalPoint();
|
||||
m_thousandsSeparator = m_locale.groupSeparator();
|
||||
SetArgSep(';');
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -228,15 +238,41 @@ void QmuParserBase::setAllowSubexpressions(bool value)
|
|||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
std::locale QmuParserBase::getLocale() const
|
||||
QLocale QmuParserBase::getLocale() const
|
||||
{
|
||||
return s_locale;
|
||||
return m_locale;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void QmuParserBase::setLocale(const std::locale &value)
|
||||
void QmuParserBase::setLocale(const QLocale &value)
|
||||
{
|
||||
s_locale = value;
|
||||
m_locale = value;
|
||||
InitCharSets();
|
||||
InitOprt();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
QChar QmuParserBase::getDecimalPoint() const
|
||||
{
|
||||
return m_decimalPoint;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void QmuParserBase::setDecimalPoint(const QChar &c)
|
||||
{
|
||||
m_decimalPoint = c;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
QChar QmuParserBase::getThousandsSeparator() const
|
||||
{
|
||||
return m_thousandsSeparator;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void QmuParserBase::setThousandsSeparator(const QChar &c)
|
||||
{
|
||||
m_thousandsSeparator = c;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -1319,7 +1355,7 @@ void QmuParserBase::CreateRPN() const
|
|||
|
||||
for (;;)
|
||||
{
|
||||
opt = m_pTokenReader->ReadNextToken(s_locale);
|
||||
opt = m_pTokenReader->ReadNextToken(m_locale, m_decimalPoint, m_thousandsSeparator);
|
||||
|
||||
switch (opt.GetCode())
|
||||
{
|
||||
|
|
|
@ -31,11 +31,10 @@
|
|||
#include <QStringList>
|
||||
#include <QVector>
|
||||
#include <QtGlobal>
|
||||
#include <locale>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <QLocale>
|
||||
|
||||
#include "../qmuparser/qmuparser_global.h"
|
||||
#include "qmuparser_global.h"
|
||||
#include "qmuparserbytecode.h"
|
||||
#include "qmuparsercallback.h"
|
||||
|
@ -81,8 +80,6 @@ public:
|
|||
int GetNumResults() const;
|
||||
void SetExpr(const QString &a_sExpr);
|
||||
void SetVarFactory(facfun_type a_pFactory, void *pUserData = nullptr);
|
||||
void SetDecSep(char_type cDecSep);
|
||||
void SetThousandsSep(char_type cThousandsSep = 0);
|
||||
void ResetLocale();
|
||||
void EnableOptimizer(bool a_bIsOn=true);
|
||||
void EnableBuiltInOprt(bool a_bIsOn=true);
|
||||
|
@ -128,12 +125,20 @@ public:
|
|||
|
||||
void setAllowSubexpressions(bool value);
|
||||
|
||||
std::locale getLocale() const;
|
||||
void setLocale(const std::locale &value);
|
||||
QLocale getLocale() const;
|
||||
void setLocale(const QLocale &value);
|
||||
|
||||
QChar getDecimalPoint() const;
|
||||
void setDecimalPoint(const QChar &c);
|
||||
|
||||
QChar getThousandsSeparator() const;
|
||||
void setThousandsSeparator(const QChar &c);
|
||||
|
||||
protected:
|
||||
static const QStringList c_DefaultOprt;
|
||||
std::locale s_locale; ///< The locale used by the parser
|
||||
QLocale m_locale;///< The locale used by the parser
|
||||
QChar m_decimalPoint;
|
||||
QChar m_thousandsSeparator;
|
||||
static bool g_DbgDumpCmdCode;
|
||||
static bool g_DbgDumpStack;
|
||||
void Init();
|
||||
|
|
|
@ -32,8 +32,8 @@
|
|||
@brief This file contains standard definitions used by the parser.
|
||||
*/
|
||||
|
||||
#define QMUP_VERSION "2.2.4"
|
||||
#define QMUP_VERSION_DATE "20140504; GC"
|
||||
#define QMUP_VERSION "2.5.0"
|
||||
#define QMUP_VERSION_DATE "20170101; GC"
|
||||
|
||||
#define QMUP_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
|
@ -64,6 +64,8 @@
|
|||
/** @brief Definition of the basic parser string type. */
|
||||
#define QMUP_STRING_TYPE std::wstring
|
||||
|
||||
class QLocale;
|
||||
|
||||
namespace qmu
|
||||
{
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -275,7 +277,8 @@ typedef qreal ( *strfun_type2 ) ( const QString &, qreal );
|
|||
typedef qreal ( *strfun_type3 ) ( const QString &, qreal, qreal );
|
||||
|
||||
/** @brief Callback used for functions that identify values in a string. */
|
||||
typedef int ( *identfun_type ) ( const QString &sExpr, int *nPos, qreal *fVal, const std::locale &s_locale );
|
||||
typedef int ( *identfun_type ) ( const QString &sExpr, int *nPos, qreal *fVal, const QLocale &locale,
|
||||
const QChar &decimal, const QChar &thousand );
|
||||
|
||||
/** @brief Callback used for variable creation factory functions. */
|
||||
typedef qreal* ( *facfun_type ) ( const QString &, void* );
|
||||
|
|
|
@ -74,9 +74,12 @@ QmuParserTester::QmuParserTester(QObject *parent)
|
|||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
int QmuParserTester::IsHexVal ( const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const std::locale &s_locale )
|
||||
int QmuParserTester::IsHexVal ( const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const QLocale &locale,
|
||||
const QChar &decimal, const QChar &thousand )
|
||||
{
|
||||
Q_UNUSED(s_locale)
|
||||
Q_UNUSED(locale)
|
||||
Q_UNUSED(decimal)
|
||||
Q_UNUSED(thousand)
|
||||
if ( a_szExpr.data()[1] == 0 || ( a_szExpr.data()[0] != '0' || a_szExpr.data()[1] != 'x' ) )
|
||||
{
|
||||
return 0;
|
||||
|
|
|
@ -293,7 +293,8 @@ private:
|
|||
}
|
||||
|
||||
// Custom value recognition
|
||||
static int IsHexVal (const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const std::locale &s_locale);
|
||||
static int IsHexVal (const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const QLocale &locale,
|
||||
const QChar &decimal, const QChar &thousand);
|
||||
|
||||
// cppcheck-suppress functionStatic
|
||||
int TestNames();
|
||||
|
|
|
@ -213,7 +213,8 @@ void QmuParserTokenReader::ReInit()
|
|||
/**
|
||||
* @brief Read the next token from the string.
|
||||
*/
|
||||
QmuParserTokenReader::token_type QmuParserTokenReader::ReadNextToken(const std::locale &s_locale)
|
||||
QmuParserTokenReader::token_type QmuParserTokenReader::ReadNextToken(const QLocale &locale, const QChar &decimal,
|
||||
const QChar &thousand)
|
||||
{
|
||||
assert ( m_pParser );
|
||||
|
||||
|
@ -245,7 +246,7 @@ QmuParserTokenReader::token_type QmuParserTokenReader::ReadNextToken(const std::
|
|||
{
|
||||
return SaveBeforeReturn ( tok ); // Check for function argument separators
|
||||
}
|
||||
if ( IsValTok ( tok, s_locale ) )
|
||||
if ( IsValTok ( tok, locale, decimal, thousand ) )
|
||||
{
|
||||
return SaveBeforeReturn ( tok ); // Check for values / constant tokens
|
||||
}
|
||||
|
@ -779,7 +780,8 @@ bool QmuParserTokenReader::IsPostOpTok ( token_type &a_Tok )
|
|||
* @param a_Tok [out] If a value token is found it will be placed here.
|
||||
* @return true if a value token has been found.
|
||||
*/
|
||||
bool QmuParserTokenReader::IsValTok ( token_type &a_Tok, const std::locale &s_locale )
|
||||
bool QmuParserTokenReader::IsValTok ( token_type &a_Tok, const QLocale &locale, const QChar &decimal,
|
||||
const QChar &thousand )
|
||||
{
|
||||
assert ( m_pConstDef );
|
||||
assert ( m_pParser );
|
||||
|
@ -815,7 +817,7 @@ bool QmuParserTokenReader::IsValTok ( token_type &a_Tok, const std::locale &s_lo
|
|||
for ( item = m_vIdentFun.begin(); item != m_vIdentFun.end(); ++item )
|
||||
{
|
||||
int iStart = m_iPos;
|
||||
if ( ( *item ) ( m_strFormula.mid ( m_iPos ), &m_iPos, &fVal, s_locale ) == 1 )
|
||||
if ( ( *item ) ( m_strFormula.mid ( m_iPos ), &m_iPos, &fVal, locale, decimal, thousand ) == 1 )
|
||||
{
|
||||
// 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2
|
||||
strTok = m_strFormula.mid ( iStart, m_iPos-iStart );
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
QChar GetArgSep() const;
|
||||
void IgnoreUndefVar(bool bIgnore);
|
||||
void ReInit();
|
||||
token_type ReadNextToken(const std::locale &s_locale);
|
||||
token_type ReadNextToken(const QLocale &locale, const QChar &decimal, const QChar &thousand);
|
||||
private:
|
||||
|
||||
/**
|
||||
|
@ -111,7 +111,7 @@ private:
|
|||
bool IsFunTok(token_type &a_Tok);
|
||||
bool IsPostOpTok(token_type &a_Tok);
|
||||
bool IsOprt(token_type &a_Tok);
|
||||
bool IsValTok(token_type &a_Tok, const std::locale &s_locale);
|
||||
bool IsValTok(token_type &a_Tok, const QLocale &locale, const QChar &decimal, const QChar &thousand);
|
||||
bool IsVarTok(token_type &a_Tok);
|
||||
bool IsStrVarTok(token_type &a_Tok);
|
||||
bool IsUndefVarTok(token_type &a_Tok);
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace qmu
|
|||
//---------------------------------------------------------------------------------------------------------------------
|
||||
QmuTokenParser::QmuTokenParser()
|
||||
{
|
||||
InitCharacterSets();
|
||||
InitCharSets();
|
||||
setAllowSubexpressions(false);//Only one expression per time
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ QmuTokenParser::QmuTokenParser()
|
|||
QmuTokenParser::QmuTokenParser(const QString &formula, bool osSeparator, bool fromUser)
|
||||
:QmuFormulaBase()
|
||||
{
|
||||
InitCharacterSets();
|
||||
InitCharSets();
|
||||
setAllowSubexpressions(false);//Only one expression per time
|
||||
SetVarFactory(AddVariable, this);
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
Calculator::Calculator()
|
||||
:QmuFormulaBase()
|
||||
{
|
||||
InitCharacterSets();
|
||||
InitCharSets();
|
||||
setAllowSubexpressions(false);//Only one expression per time
|
||||
|
||||
SetSepForEval();
|
||||
|
|
|
@ -780,6 +780,11 @@ QString VTranslateVars::FormulaFromUser(const QString &formula, bool osSeparator
|
|||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tValues.at(i) == QLocale().negativeSign())
|
||||
{// unary minus
|
||||
newFormula.replace(tKeys.at(i), 1, '-');
|
||||
}
|
||||
}
|
||||
|
||||
QLocale loc = QLocale(); // User locale
|
||||
|
@ -916,6 +921,11 @@ QString VTranslateVars::FormulaToUser(const QString &formula, bool osSeparator)
|
|||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tValues.at(i) == QChar('-'))
|
||||
{// unary minus
|
||||
newFormula.replace(tKeys.at(i), 1, QLocale().negativeSign());
|
||||
}
|
||||
}
|
||||
|
||||
QLocale loc = QLocale();// User locale
|
||||
|
@ -936,11 +946,6 @@ QString VTranslateVars::FormulaToUser(const QString &formula, bool osSeparator)
|
|||
|
||||
loc = QLocale();// To user locale
|
||||
QString dStr = loc.toString(d);// Number string in user locale
|
||||
const QChar thSep = loc.groupSeparator();
|
||||
if (thSep.isSpace())
|
||||
{
|
||||
dStr.remove(thSep);// Remove thousand separator
|
||||
}
|
||||
newFormula.replace(nKeys.at(i), nValues.at(i).length(), dStr);
|
||||
const int bias = nValues.at(i).length() - dStr.length();
|
||||
if (bias != 0)
|
||||
|
|
|
@ -53,7 +53,9 @@ SOURCES += \
|
|||
tst_vcubicbezierpath.cpp \
|
||||
tst_vgobject.cpp \
|
||||
tst_vsplinepath.cpp \
|
||||
tst_vpointf.cpp
|
||||
tst_vpointf.cpp \
|
||||
tst_readval.cpp \
|
||||
tst_vtranslatevars.cpp
|
||||
|
||||
win32-msvc*:SOURCES += stable.cpp
|
||||
|
||||
|
@ -77,7 +79,9 @@ HEADERS += \
|
|||
tst_vcubicbezierpath.h \
|
||||
tst_vgobject.h \
|
||||
tst_vsplinepath.h \
|
||||
tst_vpointf.h
|
||||
tst_vpointf.h \
|
||||
tst_readval.h \
|
||||
tst_vtranslatevars.h
|
||||
|
||||
# Set using ccache. Function enable_ccache() defined in common.pri.
|
||||
$$enable_ccache()
|
||||
|
|
|
@ -47,8 +47,11 @@
|
|||
#include "tst_vgobject.h"
|
||||
#include "tst_vsplinepath.h"
|
||||
#include "tst_vpointf.h"
|
||||
#include "tst_readval.h"
|
||||
#include "tst_vtranslatevars.h"
|
||||
|
||||
#include "../vmisc/def.h"
|
||||
#include "../qmuparser/qmudef.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
@ -82,6 +85,8 @@ int main(int argc, char** argv)
|
|||
ASSERT_TEST(new TST_VCubicBezierPath());
|
||||
ASSERT_TEST(new TST_VGObject());
|
||||
ASSERT_TEST(new TST_VPointF());
|
||||
ASSERT_TEST(new TST_ReadVal());
|
||||
ASSERT_TEST(new TST_VTranslateVars());
|
||||
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -28,12 +28,14 @@
|
|||
|
||||
#include "tst_qmutokenparser.h"
|
||||
#include "../qmuparser/qmutokenparser.h"
|
||||
#include "../vmisc/logging.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
TST_QmuTokenParser::TST_QmuTokenParser(QObject *parent)
|
||||
:QObject(parent)
|
||||
: QObject(parent),
|
||||
m_systemLocale(QLocale::system())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -56,6 +58,7 @@ void TST_QmuTokenParser::IsSingle_data()
|
|||
QTest::newRow("Correct C locale 15500") << "15500" << true;
|
||||
QTest::newRow("Correct C locale 15,500") << "15,500" << true;
|
||||
QTest::newRow("Correct C locale 15,500.1") << "15,500.1" << true;
|
||||
QTest::newRow("Correct C locale 15500.1") << "15500.1" << true;
|
||||
QTest::newRow("Not C locale 15,5") << "15,5" << false;
|
||||
QTest::newRow("Not C locale 15.500,1") << "15.500,1" << false;
|
||||
}
|
||||
|
@ -68,3 +71,95 @@ void TST_QmuTokenParser::IsSingle()
|
|||
|
||||
QCOMPARE(qmu::QmuTokenParser::IsSingle(formula), result);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_QmuTokenParser::TokenFromUser_data()
|
||||
{
|
||||
QTest::addColumn<QString>("formula");
|
||||
QTest::addColumn<bool>("result");
|
||||
QTest::addColumn<QLocale>("locale");
|
||||
|
||||
const QList<QLocale> allLocales =
|
||||
QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
|
||||
for(int i = 0; i < allLocales.size(); ++i)
|
||||
{
|
||||
const QLocale locale = allLocales.at(i);
|
||||
PrepareVal(1000.5, locale);
|
||||
PrepareVal(-1000.5, locale);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_QmuTokenParser::TokenFromUser()
|
||||
{
|
||||
QFETCH(QString, formula);
|
||||
QFETCH(bool, result);
|
||||
QFETCH(QLocale, locale);
|
||||
|
||||
QLocale::setDefault(locale);
|
||||
|
||||
QCOMPARE(IsSingleFromUser(formula), result);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_QmuTokenParser::cleanupTestCase()
|
||||
{
|
||||
QLocale::setDefault(m_systemLocale);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_QmuTokenParser::PrepareVal(qreal val, const QLocale &locale)
|
||||
{
|
||||
const QString formula = locale.toString(val);
|
||||
QString string = formula;
|
||||
QString tag = QString("%1. String '%2'").arg(locale.name()).arg(string);
|
||||
QTest::newRow(qUtf8Printable(tag)) << string << true << locale;
|
||||
|
||||
string = formula+QLatin1String("+");
|
||||
tag = QString("%1. String '%2'").arg(locale.name()).arg(string);
|
||||
QTest::newRow(qUtf8Printable(tag)) << string << false << locale;
|
||||
|
||||
string = formula+QLatin1String("+")+formula;
|
||||
tag = QString("%1. String '%2'").arg(locale.name()).arg(string);
|
||||
QTest::newRow(qUtf8Printable(tag)) << string << false << locale;
|
||||
|
||||
string = formula+QString("+б");
|
||||
tag = QString("%1. String '%2'").arg(locale.name()).arg(string);
|
||||
QTest::newRow(qUtf8Printable(tag)) << string << false << locale;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
bool TST_QmuTokenParser::IsSingleFromUser(const QString &formula)
|
||||
{
|
||||
if (formula.isEmpty())
|
||||
{
|
||||
return false;// if don't know say no
|
||||
}
|
||||
|
||||
QMap<int, QString> tokens;
|
||||
QMap<int, QString> numbers;
|
||||
|
||||
try
|
||||
{
|
||||
QScopedPointer<qmu::QmuTokenParser> cal(new qmu::QmuTokenParser(formula, true, true));
|
||||
tokens = cal->GetTokens();// Tokens (variables, measurements)
|
||||
numbers = cal->GetNumbers();// All numbers in expression
|
||||
}
|
||||
catch (const qmu::QmuParserError &e)
|
||||
{
|
||||
Q_UNUSED(e)
|
||||
return false;// something wrong with formula, say no
|
||||
}
|
||||
|
||||
// Remove "-" from tokens list if exist. If don't do that unary minus operation will broken.
|
||||
qmu::QmuFormulaBase::RemoveAll(tokens, QLocale().negativeSign());
|
||||
|
||||
if (tokens.isEmpty() && numbers.size() == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,18 +29,26 @@
|
|||
#ifndef TST_QMUTOKENPARSER_H
|
||||
#define TST_QMUTOKENPARSER_H
|
||||
|
||||
#include <QLocale>
|
||||
#include <QObject>
|
||||
|
||||
class TST_QmuTokenParser : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_DISABLE_COPY(TST_QmuTokenParser)
|
||||
explicit TST_QmuTokenParser(QObject *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void IsSingle_data();
|
||||
void IsSingle();
|
||||
void TokenFromUser_data();
|
||||
void TokenFromUser();
|
||||
void cleanupTestCase();
|
||||
private:
|
||||
Q_DISABLE_COPY(TST_QmuTokenParser)
|
||||
QLocale m_systemLocale;
|
||||
|
||||
void PrepareVal(qreal val, const QLocale &locale);
|
||||
bool IsSingleFromUser(const QString &formula);
|
||||
};
|
||||
|
||||
#endif // TST_QMUTOKENPARSER_H
|
||||
|
|
153
src/test/ValentinaTest/tst_readval.cpp
Normal file
153
src/test/ValentinaTest/tst_readval.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
/************************************************************************
|
||||
**
|
||||
** @file
|
||||
** @author Roman Telezhynskyi <dismine(at)gmail.com>
|
||||
** @date 30 12, 2016
|
||||
**
|
||||
** @brief
|
||||
** @copyright
|
||||
** This source code is part of the Valentine project, a pattern making
|
||||
** program, whose allow create and modeling patterns of clothing.
|
||||
** Copyright (C) 2016 Valentina project
|
||||
** <https://bitbucket.org/dismine/valentina> All Rights Reserved.
|
||||
**
|
||||
** Valentina 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 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** Valentina is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*************************************************************************/
|
||||
|
||||
#include "tst_readval.h"
|
||||
#include "../qmuparser/qmudef.h"
|
||||
#include "../vmisc/logging.h"
|
||||
|
||||
#include <QtTest>
|
||||
#include <limits>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
TST_ReadVal::TST_ReadVal(QObject *parent)
|
||||
: QObject(parent),
|
||||
m_systemLocale(QLocale::system())
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_ReadVal::TestReadVal_data()
|
||||
{
|
||||
QTest::addColumn<QString>("formula");
|
||||
QTest::addColumn<int>("expCount");
|
||||
QTest::addColumn<qreal>("expVal");
|
||||
QTest::addColumn<QLocale>("locale");
|
||||
|
||||
const QList<QLocale> allLocales =
|
||||
QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
|
||||
for(int i = 0; i < allLocales.size(); ++i)
|
||||
{
|
||||
const QLocale locale = allLocales.at(i);
|
||||
PrepareVal(1., locale);
|
||||
PrepareVal(1.0, locale);
|
||||
PrepareVal(-1.0, locale);
|
||||
PrepareVal(1.5, locale);
|
||||
PrepareVal(-1.5, locale);
|
||||
PrepareVal(1000.0, locale);
|
||||
PrepareVal(-1000.0, locale);
|
||||
PrepareVal(1000.5, locale);
|
||||
PrepareVal(-1000.5, locale);
|
||||
PrepareVal(std::numeric_limits<double>::max(), locale);
|
||||
PrepareVal(-std::numeric_limits<double>::max(), locale);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_ReadVal::TestReadVal()
|
||||
{
|
||||
TestVal();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_ReadVal::TestInvalidData_data()
|
||||
{
|
||||
QTest::addColumn<QString>("formula");
|
||||
QTest::addColumn<int>("expCount");
|
||||
QTest::addColumn<qreal>("expVal");
|
||||
QTest::addColumn<QLocale>("locale");
|
||||
|
||||
// Test invalid values
|
||||
const QLocale locale = QLocale::c();
|
||||
PrepareString(QString(), locale);
|
||||
PrepareString(QString("-1.000.5"), locale);
|
||||
PrepareString(QString("1.000.5"), locale);
|
||||
PrepareString(QString("-1.000,5"), locale);
|
||||
PrepareString(QString("1.000,5"), locale);
|
||||
PrepareString(QString("-1.0.00,5"), locale);
|
||||
PrepareString(QString("1.0.00,5"), locale);
|
||||
PrepareString(QString("7,5"), locale);
|
||||
PrepareString(QString("-7,5"), locale);
|
||||
PrepareString(QString("- 7,5"), locale);
|
||||
PrepareString(QString("- 7.5"), locale);
|
||||
PrepareString(QString("1,0,00.5"), locale);
|
||||
PrepareString(QString("1,,000.5"), locale);
|
||||
PrepareString(QString(",5"), locale);
|
||||
PrepareString(QString(","), locale);
|
||||
PrepareString(QString("."), locale);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_ReadVal::TestInvalidData()
|
||||
{
|
||||
TestVal();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_ReadVal::cleanupTestCase()
|
||||
{
|
||||
QLocale::setDefault(m_systemLocale);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_ReadVal::PrepareVal(qreal val, const QLocale &locale)
|
||||
{
|
||||
const QString string = locale.toString(val);
|
||||
bool ok = false;
|
||||
const double d = locale.toDouble(string, &ok);
|
||||
PrepareString(string, locale, d, string.size());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_ReadVal::PrepareString(const QString &str, const QLocale &locale, qreal val, int count)
|
||||
{
|
||||
const QString tag = QString("%1. String '%2'").arg(locale.name()).arg(str);
|
||||
QTest::newRow(qUtf8Printable(tag)) << str << count << val << locale;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_ReadVal::TestVal()
|
||||
{
|
||||
QFETCH(QString, formula);
|
||||
QFETCH(int, expCount);
|
||||
QFETCH(qreal, expVal);
|
||||
QFETCH(QLocale, locale);
|
||||
|
||||
qreal resVal = 0;
|
||||
QLocale::setDefault(locale);
|
||||
|
||||
const int resCount = ReadVal(formula, resVal, locale, locale.decimalPoint(), locale.groupSeparator());
|
||||
|
||||
QString errorMsg = QString("Conversion failed. Locale: '%1'.").arg(locale.name());
|
||||
QVERIFY2(resCount == expCount, qUtf8Printable(errorMsg));
|
||||
|
||||
if (resCount != -1)
|
||||
{
|
||||
QString errorMsg = QString("Unexpected result. Locale: '%1'.").arg(locale.name());
|
||||
QVERIFY2(QmuFuzzyComparePossibleNulls(resVal, expVal), qUtf8Printable(errorMsg));
|
||||
}
|
||||
}
|
58
src/test/ValentinaTest/tst_readval.h
Normal file
58
src/test/ValentinaTest/tst_readval.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/************************************************************************
|
||||
**
|
||||
** @file
|
||||
** @author Roman Telezhynskyi <dismine(at)gmail.com>
|
||||
** @date 30 12, 2016
|
||||
**
|
||||
** @brief
|
||||
** @copyright
|
||||
** This source code is part of the Valentine project, a pattern making
|
||||
** program, whose allow create and modeling patterns of clothing.
|
||||
** Copyright (C) 2016 Valentina project
|
||||
** <https://bitbucket.org/dismine/valentina> All Rights Reserved.
|
||||
**
|
||||
** Valentina 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 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** Valentina is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef TST_READVAL_H
|
||||
#define TST_READVAL_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QLocale>
|
||||
|
||||
class TST_ReadVal : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TST_ReadVal(QObject *parent = nullptr);
|
||||
private slots:
|
||||
void TestReadVal_data();
|
||||
void TestReadVal();
|
||||
void TestInvalidData_data();
|
||||
void TestInvalidData();
|
||||
void cleanupTestCase();
|
||||
private:
|
||||
Q_DISABLE_COPY(TST_ReadVal)
|
||||
QLocale m_systemLocale;
|
||||
|
||||
void PrepareVal(qreal val, const QLocale &locale);
|
||||
void PrepareString(const QString &str, const QLocale &locale, qreal val=0, int count=-1);
|
||||
|
||||
void TestVal();
|
||||
};
|
||||
|
||||
#endif // TST_READVAL_H
|
174
src/test/ValentinaTest/tst_vtranslatevars.cpp
Normal file
174
src/test/ValentinaTest/tst_vtranslatevars.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
/************************************************************************
|
||||
**
|
||||
** @file
|
||||
** @author Roman Telezhynskyi <dismine(at)gmail.com>
|
||||
** @date 2 1, 2017
|
||||
**
|
||||
** @brief
|
||||
** @copyright
|
||||
** This source code is part of the Valentine project, a pattern making
|
||||
** program, whose allow create and modeling patterns of clothing.
|
||||
** Copyright (C) 2017 Valentina project
|
||||
** <https://bitbucket.org/dismine/valentina> All Rights Reserved.
|
||||
**
|
||||
** Valentina 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 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** Valentina is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*************************************************************************/
|
||||
|
||||
#include "tst_vtranslatevars.h"
|
||||
#include "../vmisc/logging.h"
|
||||
#include "../vpatterndb/vtranslatevars.h"
|
||||
#include "../qmuparser/qmuparsererror.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
TST_VTranslateVars::TST_VTranslateVars(QObject *parent)
|
||||
: QObject(parent),
|
||||
m_trMs(nullptr),
|
||||
m_systemLocale(QLocale::system())
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_VTranslateVars::initTestCase()
|
||||
{
|
||||
m_trMs = new VTranslateVars();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_VTranslateVars::TestFormulaFromUser_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<QString>("output");
|
||||
QTest::addColumn<QLocale>("locale");
|
||||
|
||||
const QList<QLocale> allLocales =
|
||||
QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
|
||||
for(int i = 0; i < allLocales.size(); ++i)
|
||||
{
|
||||
PrepareValFromUser(1000.5, allLocales.at(i));
|
||||
PrepareValFromUser(-1000.5, allLocales.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_VTranslateVars::TestFormulaFromUser()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(QString, output);
|
||||
QFETCH(QLocale, locale);
|
||||
|
||||
QLocale::setDefault(locale);
|
||||
|
||||
QString result;
|
||||
try
|
||||
{
|
||||
result = m_trMs->FormulaFromUser(input, true);
|
||||
}
|
||||
catch (qmu::QmuParserError &e)// In case something bad will happen
|
||||
{
|
||||
Q_UNUSED(e)
|
||||
result = input;
|
||||
}
|
||||
|
||||
QCOMPARE(result, output);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_VTranslateVars::TestFormulaToUser_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<QString>("output");
|
||||
QTest::addColumn<QLocale>("locale");
|
||||
|
||||
const QList<QLocale> allLocales =
|
||||
QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
|
||||
for(int i = 0; i < allLocales.size(); ++i)
|
||||
{
|
||||
PrepareValToUser(1000.5, allLocales.at(i));
|
||||
PrepareValToUser(-1000.5, allLocales.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_VTranslateVars::TestFormulaToUser()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(QString, output);
|
||||
QFETCH(QLocale, locale);
|
||||
|
||||
QLocale::setDefault(locale);
|
||||
|
||||
QString result;
|
||||
try
|
||||
{
|
||||
result = m_trMs->FormulaToUser(input, true);
|
||||
}
|
||||
catch (qmu::QmuParserError &e)// In case something bad will happen
|
||||
{
|
||||
Q_UNUSED(e)
|
||||
result = input;
|
||||
}
|
||||
|
||||
QCOMPARE(result, output);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_VTranslateVars::cleanupTestCase()
|
||||
{
|
||||
delete m_trMs;
|
||||
QLocale::setDefault(m_systemLocale);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_VTranslateVars::PrepareValFromUser(double d, const QLocale &locale)
|
||||
{
|
||||
const QString formulaToSystem = QLocale::c().toString(d);
|
||||
const QString formulaFromUser = locale.toString(d);
|
||||
|
||||
PrepareVal(formulaFromUser, formulaToSystem, locale);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_VTranslateVars::PrepareValToUser(double d, const QLocale &locale)
|
||||
{
|
||||
const QString formulaFromSystem = QLocale::c().toString(d);
|
||||
const QString formulaToUser = locale.toString(d);
|
||||
|
||||
PrepareVal(formulaFromSystem, formulaToUser, locale);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void TST_VTranslateVars::PrepareVal(const QString &inputFormula, const QString &outputFormula, const QLocale &locale)
|
||||
{
|
||||
QString inputString = inputFormula;
|
||||
QString outputString = outputFormula;
|
||||
|
||||
auto PREPARE_CASE = [locale](const QString &inputString, const QString &outputString)
|
||||
{
|
||||
QString tag = QString("%1. String '%2'").arg(locale.name()).arg(inputString);
|
||||
QTest::newRow(qUtf8Printable(tag)) << inputString << outputString << locale;
|
||||
};
|
||||
|
||||
PREPARE_CASE(inputString, outputString);
|
||||
|
||||
inputString = inputFormula+QLatin1String("+")+inputFormula;
|
||||
outputString = outputFormula+QLatin1String("+")+outputFormula;
|
||||
PREPARE_CASE(inputString, outputString);
|
||||
|
||||
inputString = inputFormula+QString("+a");
|
||||
outputString = outputFormula+QString("+a");
|
||||
PREPARE_CASE(inputString, outputString);
|
||||
}
|
60
src/test/ValentinaTest/tst_vtranslatevars.h
Normal file
60
src/test/ValentinaTest/tst_vtranslatevars.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/************************************************************************
|
||||
**
|
||||
** @file
|
||||
** @author Roman Telezhynskyi <dismine(at)gmail.com>
|
||||
** @date 2 1, 2017
|
||||
**
|
||||
** @brief
|
||||
** @copyright
|
||||
** This source code is part of the Valentine project, a pattern making
|
||||
** program, whose allow create and modeling patterns of clothing.
|
||||
** Copyright (C) 2017 Valentina project
|
||||
** <https://bitbucket.org/dismine/valentina> All Rights Reserved.
|
||||
**
|
||||
** Valentina 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 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** Valentina is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef TST_VTRANSLATEVARS_H
|
||||
#define TST_VTRANSLATEVARS_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QLocale>
|
||||
#include <QObject>
|
||||
|
||||
class VTranslateVars;
|
||||
|
||||
class TST_VTranslateVars : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TST_VTranslateVars(QObject *parent = nullptr);
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void TestFormulaFromUser_data();
|
||||
void TestFormulaFromUser();
|
||||
void TestFormulaToUser_data();
|
||||
void TestFormulaToUser();
|
||||
void cleanupTestCase();
|
||||
private:
|
||||
Q_DISABLE_COPY(TST_VTranslateVars)
|
||||
VTranslateVars *m_trMs;
|
||||
QLocale m_systemLocale;
|
||||
|
||||
void PrepareValFromUser(double d, const QLocale &locale);
|
||||
void PrepareValToUser(double d, const QLocale &locale);
|
||||
void PrepareVal(const QString &inputFormula, const QString &outputFormula, const QLocale &locale);
|
||||
};
|
||||
|
||||
#endif // TST_VTRANSLATEVARS_H
|
Loading…
Reference in New Issue
Block a user