1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the Qt Linguist of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #ifndef PROFILEPARSER_H |
43 | #define PROFILEPARSER_H |
44 | |
45 | #include "proparser_global.h" |
46 | #include "proitems.h" |
47 | #include <QtCore/QHash> |
48 | #include <QtCore/QStack> |
49 | #ifdef PROPARSER_THREAD_SAFE |
50 | # include <QtCore/QMutex> |
51 | # include <QtCore/QWaitCondition> |
52 | #endif |
53 | |
54 | // Be fast even for debug builds |
55 | #ifdef __GNUC__ |
56 | # define ALWAYS_INLINE inline __attribute__((always_inline)) |
57 | #elif defined(_MSC_VER) |
58 | # define ALWAYS_INLINE __forceinline |
59 | #else |
60 | # define ALWAYS_INLINE inline |
61 | #endif |
62 | |
63 | QT_BEGIN_NAMESPACE |
64 | class PROPARSER_EXPORT ProFileParserHandler |
65 | { |
66 | public: |
67 | // Some error during parsing |
68 | virtual void parseError(const QString &filename, int lineNo, const QString &msg) = 0; |
69 | }; |
70 | |
71 | class ProFileCache; |
72 | |
73 | class PROPARSER_EXPORT ProFileParser |
74 | { |
75 | public: |
76 | // Call this from a concurrency-free context |
77 | static void initialize(); |
78 | |
79 | ProFileParser(ProFileCache *cache, ProFileParserHandler *handler); |
80 | |
81 | // fileName is expected to be absolute and cleanPath()ed. |
82 | // If contents is non-null, it will be used instead of the file's actual content |
83 | ProFile *parsedProFile(const QString &fileName, bool cache = false, |
84 | const QString *contents = 0); |
85 | ProFile *parsedProBlock(const QString &name, const QString &contents) |
86 | { return parsedProFile(name, false, &contents); } |
87 | |
88 | private: |
89 | struct BlockScope { |
90 | BlockScope() : start(0), braceLevel(0), special(false), inBranch(false) {} |
91 | BlockScope(const BlockScope &other) { *this = other; } |
92 | ushort *start; // Where this block started; store length here |
93 | int braceLevel; // Nesting of braces in scope |
94 | bool special; // Single-line conditionals inside loops, etc. cannot have else branches |
95 | bool inBranch; // The 'else' branch of the previous TokBranch is still open |
96 | }; |
97 | |
98 | enum ScopeState { |
99 | StNew, // Fresh scope |
100 | StCtrl, // Control statement (for or else) met on current line |
101 | StCond // Conditionals met on current line |
102 | }; |
103 | |
104 | enum Context { CtxTest, CtxValue, CtxArgs }; |
105 | struct ParseCtx { |
106 | int parens; // Nesting of non-functional parentheses |
107 | int argc; // Number of arguments in current function call |
108 | int wordCount; // Number of words in current expression |
109 | Context context; |
110 | ushort quote; // Enclosing quote type |
111 | ushort terminator; // '}' if replace function call is braced, ':' if test function |
112 | }; |
113 | |
114 | bool read(ProFile *pro); |
115 | bool read(ProFile *pro, const QString &content); |
116 | |
117 | ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok); |
118 | ALWAYS_INLINE void putBlockLen(ushort *&tokPtr, uint len); |
119 | ALWAYS_INLINE void putBlock(ushort *&tokPtr, const ushort *buf, uint len); |
120 | void putHashStr(ushort *&pTokPtr, const ushort *buf, uint len); |
121 | void finalizeHashStr(ushort *buf, uint len); |
122 | void putLineMarker(ushort *&tokPtr); |
123 | void finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount); |
124 | void finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc); |
125 | void finalizeTest(ushort *&tokPtr); |
126 | void bogusTest(ushort *&tokPtr); |
127 | void enterScope(ushort *&tokPtr, bool special, ScopeState state); |
128 | void leaveScope(ushort *&tokPtr); |
129 | void flushCond(ushort *&tokPtr); |
130 | void flushScopes(ushort *&tokPtr); |
131 | |
132 | void parseError(const QString &msg) const; |
133 | |
134 | // Current location |
135 | ProFile *m_proFile; |
136 | int m_lineNo; |
137 | |
138 | QStack<BlockScope> m_blockstack; |
139 | ScopeState m_state; |
140 | int m_markLine; // Put marker for this line |
141 | bool m_inError; // Current line had a parsing error; suppress followup error messages |
142 | bool m_canElse; // Conditionals met on previous line, but no scope was opened |
143 | bool m_invert; // Pending conditional is negated |
144 | enum { NoOperator, AndOperator, OrOperator } m_operator; // Pending conditional is ORed/ANDed |
145 | |
146 | QString m_tmp; // Temporary for efficient toQString |
147 | |
148 | ProFileCache *m_cache; |
149 | ProFileParserHandler *m_handler; |
150 | |
151 | // This doesn't help gcc 3.3 ... |
152 | template<typename T> friend class QTypeInfo; |
153 | |
154 | friend class ProFileCache; |
155 | }; |
156 | |
157 | class PROPARSER_EXPORT ProFileCache |
158 | { |
159 | public: |
160 | ProFileCache() {} |
161 | ~ProFileCache(); |
162 | |
163 | void discardFile(const QString &fileName); |
164 | void discardFiles(const QString &prefix); |
165 | |
166 | private: |
167 | struct Entry { |
168 | ProFile *pro; |
169 | #ifdef PROPARSER_THREAD_SAFE |
170 | struct Locker { |
171 | Locker() : waiters(0), done(false) {} |
172 | QWaitCondition cond; |
173 | int waiters; |
174 | bool done; |
175 | }; |
176 | Locker *locker; |
177 | #endif |
178 | }; |
179 | |
180 | QHash<QString, Entry> parsed_files; |
181 | #ifdef PROPARSER_THREAD_SAFE |
182 | QMutex mutex; |
183 | #endif |
184 | |
185 | friend class ProFileParser; |
186 | }; |
187 | |
188 | #if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) |
189 | Q_DECLARE_TYPEINFO(ProFileParser::BlockScope, Q_MOVABLE_TYPE); |
190 | Q_DECLARE_TYPEINFO(ProFileParser::Context, Q_PRIMITIVE_TYPE); |
191 | #endif |
192 | |
193 | QT_END_NAMESPACE |
194 | |
195 | #endif // PROFILEPARSER_H |
196 | |