Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2** Copyright (C) 2017 Ford Motor Company.
3** All rights reserved.
4**
5** Copyright (C) 2017 The Qt Company Ltd.
6** Contact: https://www.qt.io/licensing/
7**
8** This file is part of the QtRemoteObjects module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial License Usage
12** Licensees holding valid commercial Qt licenses may use this file in
13** accordance with the commercial license agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and The Qt Company. For licensing terms
16** and conditions see https://www.qt.io/terms-conditions. For further
17** information use the contact form at https://www.qt.io/contact-us.
18**
19** GNU Lesser General Public License Usage
20** Alternatively, this file may be used under the terms of the GNU Lesser
21** General Public License version 3 as published by the Free Software
22** Foundation and appearing in the file LICENSE.LGPL3 included in the
23** packaging of this file. Please review the following information to
24** ensure the GNU Lesser General Public License version 3 requirements
25** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26**
27** GNU General Public License Usage
28** Alternatively, this file may be used under the terms of the GNU
29** General Public License version 2.0 or (at your option) the GNU General
30** Public license version 3 or any later version approved by the KDE Free
31** Qt Foundation. The licenses are as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33** included in the packaging of this file. Please review the following
34** information to ensure the GNU General Public License requirements will
35** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36** https://www.gnu.org/licenses/gpl-3.0.html.
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#ifndef QREGEXPARSER_H
43#define QREGEXPARSER_H
44
45#include <QtCore/qshareddata.h>
46#include <QtCore/qvarlengtharray.h>
47#include <QtCore/qvariant.h>
48#ifdef QT_BOOTSTRAPPED
49# include <QtCore/qregexp.h>
50# define REGEX QRegExp
51#else
52# include <QtCore/qregularexpression.h>
53# define REGEX QRegularExpression
54#endif
55#include <QtCore/qmap.h>
56#include <QtCore/qfile.h>
57#include <QtCore/qtextstream.h>
58#include <QtCore/qdebug.h>
59
60struct MatchCandidate {
61 MatchCandidate(const QString &n, const QString &t, int i) : name(n), matchText(t), index(i) {}
62 QString name;
63 QString matchText;
64 int index;
65};
66
67QT_BEGIN_NAMESPACE
68
69template <typename _Parser, typename _Table>
70class QRegexParser: protected _Table
71{
72public:
73 QRegexParser(int maxMatchLen=4096);
74 virtual ~QRegexParser();
75
76 virtual bool parse();
77
78 virtual void reset() {}
79
80 inline QVariant &sym(int index);
81
82 void setBuffer(const QString &buffer);
83
84 void setBufferFromDevice(QIODevice *device);
85
86 void setDebug();
87
88 QString errorString() const
89 {
90 return m_errorString;
91 }
92
93 void setErrorString(const QString &error)
94 {
95 m_errorString = error;
96 qWarning() << m_errorString;
97 }
98
99 inline const QMap<QString, QString>& captured() const
100 {
101 return m_captured;
102 }
103
104 inline bool isDebug() const
105 {
106 return m_debug;
107 }
108
109 inline int lineNumber() const
110 {
111 return m_lineno;
112 }
113
114private:
115 int nextToken();
116
117 inline bool consumeRule(int rule)
118 {
119 return static_cast<_Parser*> (this)->consumeRule(rule);
120 }
121
122 enum { DefaultStackSize = 128 };
123
124 struct Data: public QSharedData
125 {
126 Data(): stackSize (DefaultStackSize), tos (0) {}
127
128 QVarLengthArray<int, DefaultStackSize> stateStack;
129 QVarLengthArray<QVariant, DefaultStackSize> parseStack;
130 int stackSize;
131 int tos;
132
133 void reallocateStack() {
134 stackSize <<= 1;
135 stateStack.resize(stackSize);
136 parseStack.resize(stackSize);
137 }
138 };
139
140 inline QString escapeString(QString s)
141 {
142 return s.replace(c: QLatin1Char('\n'), after: QLatin1String("\\n")).replace(c: QLatin1Char('\t'), after: QLatin1String("\\t"));
143 }
144
145 QSharedDataPointer<Data> d;
146
147 QList<REGEX> m_regexes;
148#ifndef QT_BOOTSTRAPPED
149 QMap<QChar, QList<int> > regexCandidates;
150#endif
151 QList<int> m_tokens;
152 QString m_buffer, m_lastMatchText;
153 int m_loc, m_lastNewlinePosition;
154 int m_lineno;
155 int m_debug;
156 QStringList m_tokenNames;
157 QMap<QString, QString> m_captured;
158 int m_maxMatchLen;
159 QString m_errorString;
160 QVector<QMap<int, QString> > m_names; //storage for match names
161};
162
163template <typename _Parser, typename _Table>
164inline QVariant &QRegexParser<_Parser, _Table>::sym(int n)
165{
166 return d->parseStack [d->tos + n - 1];
167}
168
169template <typename _Parser, typename _Table>
170QRegexParser<_Parser, _Table>::~QRegexParser()
171{
172}
173
174template <typename _Parser, typename _Table>
175bool QRegexParser<_Parser, _Table>::parse()
176{
177 m_errorString.clear();
178 reset();
179 const int INITIAL_STATE = 0;
180
181 d->tos = 0;
182 d->reallocateStack();
183
184 int act = d->stateStack[++d->tos] = INITIAL_STATE;
185 int token = -1;
186
187 Q_FOREVER {
188 if (token == -1 && - _Table::TERMINAL_COUNT != _Table::action_index[act])
189 token = nextToken();
190
191 act = _Table::t_action(act, token);
192
193 if (d->stateStack[d->tos] == _Table::ACCEPT_STATE)
194 return true;
195
196 else if (act > 0) {
197 if (++d->tos == d->stackSize)
198 d->reallocateStack();
199
200 d->parseStack[d->tos] = d->parseStack[d->tos - 1];
201 d->stateStack[d->tos] = act;
202 token = -1;
203 }
204
205 else if (act < 0) {
206 int r = - act - 1;
207 d->tos -= _Table::rhs[r];
208 act = d->stateStack[d->tos++];
209 if (!consumeRule(rule: r))
210 return false;
211 act = d->stateStack[d->tos] = _Table::nt_action(act, _Table::lhs[r] - _Table::TERMINAL_COUNT);
212 }
213
214 else break;
215 }
216
217 setErrorString(QStringLiteral("Unknown token encountered"));
218 return false;
219}
220
221template <typename _Parser, typename _Table>
222QRegexParser<_Parser, _Table>::QRegexParser(int maxMatchLen) : d(new Data()), m_loc(0), m_lastNewlinePosition(0), m_lineno(1), m_debug(0), m_maxMatchLen(maxMatchLen)
223{
224 REGEX re(QStringLiteral("\\[([_a-zA-Z][_0-9a-zA-Z]*)(,\\s*M)?\\](.+)$"));
225#ifdef QT_BOOTSTRAPPED
226 REGEX nameMatch(QStringLiteral("\\((\\?<(.*)>).+\\)"));
227 nameMatch.setMinimal(true);
228#else
229 re.optimize();
230#endif
231 QMap<QString, int> token_lookup;
232 QMap<int, QString> names;
233 for (int i = 1; i < _Table::lhs[0]; i++) {
234 const QString text = QLatin1String(_Table::spell[i]);
235 names.clear();
236#ifdef QT_BOOTSTRAPPED
237 if (re.indexIn(text) == 0) {
238 const QString token = re.cap(1);
239 const bool multiline = re.cap(2).length() > 0;
240 QString pattern = re.cap(3);
241 //We need to identify/remove any match names in the pattern, since
242 //QRegExp doesn't support that feature
243 int pos = 0, counter = 1, loc = nameMatch.indexIn(pattern, pos);
244 while (loc >= 0) {
245 const QString res = nameMatch.cap(2);
246 if (!res.isEmpty()) {
247 names.insert(counter, res);
248 pattern.remove(nameMatch.cap(1));
249 }
250 pos += loc + nameMatch.matchedLength() - nameMatch.cap(1).length();
251 loc = nameMatch.indexIn(pattern, pos);
252 ++counter;
253 }
254 //We need to use indexIn, but that will search past the location we
255 //pass in. So prepend '^' and use QRegExp::CaretAtOffset.
256 if (pattern.at(0) != QChar(QLatin1Char('^')))
257 pattern.prepend(QChar(QLatin1Char('^')));
258#else
259 QRegularExpressionMatch match = re.match(subject: text, offset: 0, matchType: QRegularExpression::NormalMatch, matchOptions: QRegularExpression::DontCheckSubjectStringMatchOption);
260 if (match.hasMatch()) {
261 const QString token = match.captured(nth: 1);
262 const bool multiline = match.captured(nth: 2).length() > 0;
263 const QString pattern = match.captured(nth: 3);
264#endif
265 m_tokenNames.append(t: token);
266 int index = i;
267 if (token_lookup.contains(key: token))
268 index = token_lookup[token];
269 else
270 token_lookup[token] = i;
271#ifdef QT_BOOTSTRAPPED
272 if (multiline)
273 qWarning() << "The multiline grammar option is ignore in force_bootstrap mode.";
274#endif
275 REGEX pat(pattern);
276#ifndef QT_BOOTSTRAPPED
277 if (multiline)
278 pat.setPatternOptions(QRegularExpression::DotMatchesEverythingOption);
279#endif
280 if (!pat.isValid())
281 qCritical() << "Pattern error for token #" << i << "for" << text << "pattern =" << pat << ":" << pat.errorString();
282 else {
283#ifndef QT_BOOTSTRAPPED
284 pat.optimize();
285 int counter = 0;
286 const auto namedCaptureGroups = pat.namedCaptureGroups();
287 for (const QString &name : namedCaptureGroups) {
288 if (!name.isEmpty())
289 names.insert(key: counter, value: name);
290 ++counter;
291 }
292#endif
293 m_names.append(t: names);
294 m_regexes.append(t: pat);
295 if (token.startsWith(s: QLatin1String("ignore")))
296 m_tokens.append(t: -1);
297 else
298 m_tokens.append(t: index);
299 }
300 } else {
301 qCritical() << "Error parsing regex at token #" << i << "for" << text << "Invalid syntax";
302 }
303 }
304}
305
306template <typename _Parser, typename _Table>
307void QRegexParser<_Parser, _Table>::setBuffer(const QString &buffer)
308{
309 m_buffer = buffer;
310}
311
312template <typename _Parser, typename _Table>
313void QRegexParser<_Parser, _Table>::setBufferFromDevice(QIODevice *device)
314{
315 QTextStream in(device);
316 m_buffer = in.readAll();
317}
318
319template <typename _Parser, typename _Table>
320void QRegexParser<_Parser, _Table>::setDebug()
321{
322 m_debug = true;
323 for (int r = 0; r < _Table::RULE_COUNT; ++r)
324 {
325 int ridx = _Table::rule_index[r];
326 int _rhs = _Table::rhs[r];
327 qDebug("%3d) %s ::=", r + 1, _Table::spell[_Table::rule_info[ridx]]);
328 ++ridx;
329 for (int i = ridx; i < ridx + _rhs; ++i)
330 {
331 int symbol = _Table::rule_info[i];
332 if (symbol > 0 && symbol < _Table::lhs[0])
333 qDebug(msg: " token_%s (pattern = %s)",qPrintable(m_tokenNames[symbol-1]),qPrintable(m_regexes[symbol-1].pattern()));
334 else if (const char *name = _Table::spell[symbol])
335 qDebug(msg: " %s", name);
336 else
337 qDebug(msg: " #%d", symbol);
338 }
339 qDebug();
340 }
341}
342
343template <typename _Parser, typename _Table>
344int QRegexParser<_Parser, _Table>::nextToken()
345{
346 static const REGEX newline(QLatin1String("(\\n)"));
347 int token = -1;
348 while (token < 0)
349 {
350 if (m_loc == m_buffer.size())
351 return _Table::EOF_SYMBOL;
352
353 //Check m_lastMatchText for newlines and update m_lineno
354 //This isn't necessary, but being able to provide the line # and character #
355 //where the match is failing sure makes building/debugging grammars easier.
356#ifdef QT_BOOTSTRAPPED
357 int loc = 0, pos = newline.indexIn(m_lastMatchText, loc);
358 while (pos >= 0) {
359 m_lineno++;
360 loc += pos + 1;
361 m_lastNewlinePosition += pos + 1;
362 pos = newline.indexIn(m_lastMatchText, loc);
363 }
364#else //QT_BOOTSTRAPPED
365 QRegularExpressionMatchIterator matches = newline.globalMatch(subject: m_lastMatchText);
366 while (matches.hasNext()) {
367 m_lineno++;
368 QRegularExpressionMatch match = matches.next();
369 if (!matches.hasNext())
370 m_lastNewlinePosition += match.capturedEnd();
371 }
372#endif //!QT_BOOTSTRAPPED
373 if (m_debug) {
374 qDebug();
375 qDebug() << "nextToken loop, line =" << m_lineno
376 << "line position =" << m_loc - m_lastNewlinePosition
377 << "next 5 characters =" << escapeString(s: m_buffer.mid(position: m_loc, n: 5));
378 }
379 int best = -1, maxLen = -1;
380#ifndef QT_BOOTSTRAPPED
381 QRegularExpressionMatch bestRegex;
382#endif
383
384 //Find the longest match.
385 //If more than one are the same (longest) length, return the first one in
386 //the order defined.
387 QList<MatchCandidate> candidates;
388#ifndef QT_BOOTSTRAPPED
389 {
390 //We used PCRE's PartialMatch to eliminate most of the regexes by the first
391 //character, so we keep a regexCandidates map with the list of possible regexes
392 //based on initial characters found so far.
393 const QChar nextChar = m_buffer.at(i: m_loc);
394 //Populate the list if we haven't seeen this character before
395 if (!regexCandidates.contains(key: nextChar)) {
396# if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
397 const QStringRef tmp = m_buffer.midRef(position: m_loc,n: 1);
398# else
399 const QString tmp = m_buffer.mid(m_loc,1);
400# endif
401 int i = 0;
402 regexCandidates[nextChar] = QList<int>();
403 for (const QRegularExpression &re : qAsConst(t&: m_regexes))
404 {
405 QRegularExpressionMatch match = re.match(subjectRef: tmp, offset: 0, matchType: QRegularExpression::PartialPreferFirstMatch, matchOptions: QRegularExpression::DontCheckSubjectStringMatchOption);
406 //qDebug() << nextChar << tmp << match.hasMatch() << match.hasPartialMatch() << re.pattern();
407 if (match.hasMatch() || match.hasPartialMatch())
408 regexCandidates[nextChar] << i;
409 i++;
410 }
411 }
412 const auto indices = regexCandidates.value(akey: nextChar);
413 for (int i : indices)
414 {
415 //Seems like I should be able to run the regex on the entire string, but performance is horrible
416 //unless I use a substring.
417 //QRegularExpressionMatch match = m_regexes[i].match(m_buffer, m_loc, QRegularExpression::NormalMatch, QRegularExpression::AnchoredMatchOption);
418# if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
419 QRegularExpressionMatch match = m_regexes.at(i).match(subjectRef: m_buffer.midRef(position: m_loc, n: m_maxMatchLen), offset: 0, matchType: QRegularExpression::NormalMatch, matchOptions: QRegularExpression::AnchoredMatchOption | QRegularExpression::DontCheckSubjectStringMatchOption);
420# else
421 QRegularExpressionMatch match = m_regexes.at(i).match(m_buffer.mid(m_loc, m_maxMatchLen), 0, QRegularExpression::NormalMatch, QRegularExpression::AnchoredMatchOption | QRegularExpression::DontCheckSubjectStringMatchOption);
422# endif
423 if (match.hasMatch()) {
424 if (m_debug)
425 candidates << MatchCandidate(m_tokenNames[i], match.captured(), i);
426 if (match.capturedLength() > maxLen) {
427 best = i;
428 maxLen = match.capturedLength();
429 bestRegex = match;
430 }
431 }
432 }
433 }
434#else
435 {
436 int i = 0;
437 for (const QRegExp &r : qAsConst(m_regexes))
438 {
439 if (r.indexIn(m_buffer, m_loc, QRegExp::CaretAtOffset) == m_loc) {
440 if (m_debug)
441 candidates << MatchCandidate(m_tokenNames[i], r.cap(0), i);
442 if (r.matchedLength() > maxLen) {
443 best = i;
444 maxLen = r.matchedLength();
445 }
446 }
447 ++i;
448 }
449 }
450#endif
451 if (best < 0) {
452 setErrorString(QLatin1String("Error generating tokens from file, next characters >%1<").arg(args: m_buffer.midRef(position: m_loc, n: 15)));
453 return -1;
454 } else {
455 const QMap<int, QString> &map = m_names.at(i: best);
456 if (!map.isEmpty())
457 m_captured.clear();
458 for (auto iter = map.cbegin(), end = map.cend(); iter != end; ++iter) {
459#ifdef QT_BOOTSTRAPPED
460 m_captured.insert(iter.value(), m_regexes.at(best).cap(iter.key()));
461#else
462 m_captured.insert(key: iter.value(), value: bestRegex.captured(nth: iter.key()));
463#endif
464 }
465 if (m_debug) {
466 qDebug() << "Match candidates:";
467 for (const MatchCandidate &m : qAsConst(t&: candidates)) {
468 QLatin1String result = m.index == best ? QLatin1String(" * ") : QLatin1String(" ");
469 qDebug() << qPrintable(result) << qPrintable(m.name) << qPrintable(escapeString(m.matchText));
470 }
471 }
472 m_loc += maxLen;
473 if (m_tokens.at(i: best) >= 0)
474 token = m_tokens.at(i: best);
475#ifdef QT_BOOTSTRAPPED
476 m_lastMatchText = m_regexes.at(best).cap(0);
477#else
478 m_lastMatchText = bestRegex.captured(nth: 0);
479#endif
480 }
481 }
482 return token;
483}
484
485QT_END_NAMESPACE
486
487#endif // QREGEXPARSER_H
488

Warning: That file was not part of the compilation database. It may have many parsing errors.

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtremoteobjects/src/repparser/qregexparser.h