Seamly2D
Code documentation
qmudef.cpp
Go to the documentation of this file.
1 /***************************************************************************************************
2  **
3  ** Copyright (C) 2016 Roman Telezhynskyi <dismine(at)gmail.com>
4  **
5  ** Permission is hereby granted, free of charge, to any person obtaining a copy of this
6  ** software and associated documentation files (the "Software"), to deal in the Software
7  ** without restriction, including without limitation the rights to use, copy, modify,
8  ** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9  ** permit persons to whom the Software is furnished to do so, subject to the following conditions:
10  **
11  ** The above copyright notice and this permission notice shall be included in all copies or
12  ** substantial portions of the Software.
13  **
14  ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15  ** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16  ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17  ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  **
20  ******************************************************************************************************/
21 
22 #include "qmudef.h"
23 
24 #include <QLocale>
25 #include <QSet>
26 
27 enum State
28 {
29  Init = 0,
30  Sign = 1,
31  Thousand = 2,
32  Mantissa = 3,
33  Dot = 4,
34  Abscissa = 5,
35  ExpMark = 6,
36  ExpSign = 7,
37  Exponent = 8,
38  Done = 9
39 };
40 
42 {
43  InputSign = 1,
46  InputDot = 4,
47  InputExp = 5
48 };
49 
50 static const QChar QmuEOF = QChar(static_cast<ushort>(0xffff)); //guaranteed not to be a character.
51 
52 //---------------------------------------------------------------------------------------------------------------------
53 static QChar GetChar(const QString &formula, int &index)
54 {
55  if (index >= formula.size())
56  {
57  return QmuEOF;
58  }
59 
60  return formula.at(index++);
61 }
62 
63 //---------------------------------------------------------------------------------------------------------------------
64 static QChar EatWhiteSpace(const QString &formula, int &index)
65 {
66  QChar c;
67  do
68  {
69  c = GetChar(formula, index);
70  }
71  while ( c != QmuEOF && c.isSpace() );
72 
73  return c;
74 }
75 
76 //---------------------------------------------------------------------------------------------------------------------
77 static int CheckChar(QChar &c, const QLocale &locale, const QChar &decimal, const QChar &thousand)
78 {
79  INIT_LOCALE_VARIABLES(locale);
80  Q_UNUSED(decimalPoint)
81  Q_UNUSED(groupSeparator)
82 
83  if (c == positiveSign)
84  {
85  c = '+';
86  return InputToken::InputSign;
87  }
88  else if (c == negativeSign)
89  {
90  c = '-';
91  return InputToken::InputSign;
92  }
93  else if (c == sign0)
94  {
95  c = '0';
97  }
98  else if (c == sign1)
99  {
100  c = '1';
101  return InputToken::InputDigit;
102  }
103  else if (c == sign2)
104  {
105  c = '2';
106  return InputToken::InputDigit;
107  }
108  else if (c == sign3)
109  {
110  c = '3';
111  return InputToken::InputDigit;
112  }
113  else if (c == sign4)
114  {
115  c = '4';
116  return InputToken::InputDigit;
117  }
118  else if (c == sign5)
119  {
120  c = '5';
121  return InputToken::InputDigit;
122  }
123  else if (c == sign6)
124  {
125  c = '6';
126  return InputToken::InputDigit;
127  }
128  else if (c == sign7)
129  {
130  c = '7';
131  return InputToken::InputDigit;
132  }
133  else if (c == sign8)
134  {
135  c = '8';
136  return InputToken::InputDigit;
137  }
138  else if (c == sign9)
139  {
140  c = '9';
141  return InputToken::InputDigit;
142  }
143  else if (c == decimal)
144  {
145  return InputToken::InputDot;
146  }
147  else if (c == thousand)
148  {
150  }
151  else if (c == expLower)
152  {
153  c = 'e';
154  return InputToken::InputExp;
155  }
156  else if (c == expUpper)
157  {
158  c = 'E';
159  return InputToken::InputExp;
160  }
161  else
162  {
163  return 0;
164  }
165 
166  return 0;
167 }
168 
169 //---------------------------------------------------------------------------------------------------------------------
170 int ReadVal(const QString &formula, qreal &val, const QLocale &locale, const QChar &decimal, const QChar &thousand)
171 {
172  // Must not be equal
173  if (decimal == thousand || formula.isEmpty())
174  {
175  val = 0;
176  return -1;
177  }
178 
179  INIT_LOCALE_VARIABLES(locale);
180  Q_UNUSED(decimalPoint)
181  Q_UNUSED(groupSeparator)
182 
183  QSet<QChar> reserved;
184  reserved << positiveSign
185  << negativeSign
186  << sign0
187  << sign1
188  << sign2
189  << sign3
190  << sign4
191  << sign5
192  << sign6
193  << sign7
194  << sign8
195  << sign9
196  << expUpper
197  << expLower;
198 
199  if (reserved.contains(decimal) || reserved.contains(thousand))
200  {
201  val = 0;
202  return -1;
203  }
204 
205  // row - current state, column - new state
206  static uchar table[9][6] =
207  {
208  /* None InputSign InputThousand InputDigit InputDot InputExp */
209  { 0, State::Sign, 0, State::Mantissa, State::Dot, 0, }, // Init
210  { 0, 0, 0, State::Mantissa, State::Dot, 0, }, // Sign
211  { 0, 0, 0, State::Mantissa, 0, 0, }, // Thousand
213  { 0, 0, 0, State::Abscissa, 0, 0, }, // Dot
214  { State::Done, State::Done, 0, State::Abscissa, 0, State::ExpMark,}, // Abscissa
215  { 0, State::ExpSign, 0, State::Exponent, 0, 0, }, // ExpMark
216  { 0, 0, 0, State::Exponent, 0, 0, }, // ExpSign
217  { State::Done, 0, 0, State::Exponent, 0, State::Done } // Exponent
218  };
219 
220  int state = State::Init; // parse state
221  QString buf;
222 
223  int index = 0; // start position
224  QChar c = EatWhiteSpace(formula, index);
225 
226  while ( true )
227  {
228  const int input = CheckChar(c, locale, decimal, thousand);// input token
229 
230  state = table[state][input];
231 
232  if (state == 0)
233  {
234  val = 0;
235  return -1;
236  }
237  else if (state == Done)
238  {
239  // Convert to C locale
240  QLocale cLocale(QLocale::C);
241  const QChar cDecimal = cLocale.decimalPoint();
242  const QChar cThousand = cLocale.groupSeparator();
243  if (locale != cLocale && (cDecimal != decimal || cThousand != thousand))
244  {
245  if (decimal == cThousand)
246  {// Handle reverse to C locale case: thousand '.', decimal ','
247  const QChar tmpThousand = '@';
248  buf.replace(thousand, tmpThousand);
249  buf.replace(decimal, cDecimal);
250  buf.replace(tmpThousand, cThousand);
251  }
252  else
253  {
254  buf.replace(thousand, cThousand);
255  buf.replace(decimal, cDecimal);
256  }
257  }
258 
259  bool ok = false;
260  const double d = cLocale.toDouble(buf, &ok);
261  if (ok)
262  {
263  val = d;
264  return buf.size();
265  }
266  else
267  {
268  val = 0;
269  return -1;
270  }
271  }
272 
273  buf.append(c);
274  c = GetChar(formula, index);
275  }
276 
277  return -1;
278 }
279 
280 //---------------------------------------------------------------------------------------------------------------------
281 QString NameRegExp()
282 {
283  static QString regex;
284 
285  if (regex.isEmpty())
286  {
287  const QList<QLocale> allLocales =
288  QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
289 
290  QString positiveSigns;
291  QString negativeSigns;
292  QString decimalPoints;
293  QString groupSeparators;
294 
295  for(int i = 0; i < allLocales.size(); ++i)
296  {
297  if (not positiveSigns.contains(allLocales.at(i).positiveSign()))
298  {
299  positiveSigns.append(allLocales.at(i).positiveSign());
300  }
301 
302  if (not negativeSigns.contains(allLocales.at(i).negativeSign()))
303  {
304  negativeSigns.append(allLocales.at(i).negativeSign());
305  }
306 
307  if (not decimalPoints.contains(allLocales.at(i).decimalPoint()))
308  {
309  decimalPoints.append(allLocales.at(i).decimalPoint());
310  }
311 
312  if (not groupSeparators.contains(allLocales.at(i).groupSeparator()))
313  {
314  groupSeparators.append(allLocales.at(i).groupSeparator());
315  }
316  }
317 
318  negativeSigns.replace('-', "\\-");
319  groupSeparators.remove('\'');
320 
321  //Same regexp in pattern.xsd shema file. Don't forget to synchronize.
322  // \p{Nd} - \p{Decimal_Digit_Number}
323  // \p{Zs} - \p{Space_Separator}
324  regex = QString("^([^\\p{Nd}\\p{Zs}*/&|!<>^\\()%1%2%3%4=?:;'\"]){1,1}"
325  "([^\\p{Zs}*/&|!<>^\\()%1%2%3%4=?:;\"]){0,}$")
326  .arg(negativeSigns).arg(positiveSigns).arg(decimalPoints).arg(groupSeparators);
327  }
328 
329  return regex;
330 }
QString NameRegExp()
Definition: qmudef.cpp:281
static const QChar QmuEOF
Definition: qmudef.cpp:50
InputToken
Definition: qmudef.cpp:42
@ InputThousand
Definition: qmudef.cpp:44
@ InputDigit
Definition: qmudef.cpp:45
@ InputSign
Definition: qmudef.cpp:43
@ InputExp
Definition: qmudef.cpp:47
@ InputDot
Definition: qmudef.cpp:46
static QChar GetChar(const QString &formula, int &index)
Definition: qmudef.cpp:53
State
Definition: qmudef.cpp:28
@ Mantissa
Definition: qmudef.cpp:32
@ Init
Definition: qmudef.cpp:29
@ Thousand
Definition: qmudef.cpp:31
@ ExpSign
Definition: qmudef.cpp:36
@ Sign
Definition: qmudef.cpp:30
@ Exponent
Definition: qmudef.cpp:37
@ Done
Definition: qmudef.cpp:38
@ ExpMark
Definition: qmudef.cpp:35
@ Abscissa
Definition: qmudef.cpp:34
@ Dot
Definition: qmudef.cpp:33
int ReadVal(const QString &formula, qreal &val, const QLocale &locale, const QChar &decimal, const QChar &thousand)
Definition: qmudef.cpp:170
static QChar EatWhiteSpace(const QString &formula, int &index)
Definition: qmudef.cpp:64
static int CheckChar(QChar &c, const QLocale &locale, const QChar &decimal, const QChar &thousand)
Definition: qmudef.cpp:77
#define INIT_LOCALE_VARIABLES(locale)
Definition: qmudef.h:38