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.

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