aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/parser/parsercontext.h
blob: d52c021e4f5bd88c34763a8e35609e59fae38c85 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#ifndef PARSERCONTEXT_H
#define PARSERCONTEXT_H

#include "ast/sqlitequery.h"
#include "parser.h"
#include "../dialect.h"

#include <QHash>
#include <QList>
#include <QSet>

class ParserError;

/**
 * @brief Parser context for SQL parsing process
 * This class should not be used outside of @class Parser.
 */
class ParserContext
{
    friend class Parser;

    public:
        /**
         * @brief Releases all internal resources.
         */
        virtual ~ParserContext();

        /**
         * @brief Adds parsed query to collection of parsed queries.
         * @param query Parsed query (AST object).
         *
         * This is called by Lemon parser.
         */
        void addQuery(SqliteQuery* query);

        /**
         * @brief Stores error at given token with given message.
         * @param token Token at which the error occurred.
         * @param text Error message.
         *
         * This is called by Lemon parser.
         */
        void error(TokenPtr token, const QString& text);

        /**
         * @overload
         */
        void error(Token* token, const QString& text);

        /**
         * @brief Stores error with given message.
         * @param text Error message.
         *
         * This method is used to report an error not related to specific token,
         * like when Lemon's stack would get exceeded (which is very unlikely).
         *
         * This is called by Lemon parser.
         *
         * @overload
         */
        void error(const QString& text);

        /**
         * @brief Stores error with most recently parsed token and given message.
         * @param text Error message.
         *
         * Lemon parser calls it when it found out that the error started at the token before.
         *
         * This is just a minor error, so it will be ognored if ignoreMinorErrors is set.
         */
        void minorErrorAfterLastToken(const QString& text);

        /**
         * @brief Marks next token to be parsed with given error message.
         * @param text Error message.
         *
         * Lemon parser calls it when it knows that any next token will be an error.
         *
         * This is just a minor error, so it will be ognored if ignoreMinorErrors is set.
         */
        void minorErrorBeforeNextToken(const QString& text);

        /**
         * @brief Stores error message for most recently parsed token.
         * @param text Error message.
         *
         * Lemon parser calls it when critical error occurred at the most recently parsed token.
         */
        void errorAfterLastToken(const QString& text);

        /**
         * @brief Stores error message for the next token to be parsed.
         * @param text Error message.
         *
         * Lemon parser calls it when critical error is about to happen at any next token.
         */
        void errorBeforeNextToken(const QString& text);

        /**
         * @brief Reports parsing error at given token position.
         * @param text Error message.
         * @param pos Position relative to after the last token. -1 means the last token, -2 the token before it and so on. -1 is default.
         *
         * This method is only useful when we know exactly which token was problematic. If error relates to some already wrapped
         * syntax rule, it may have many tokens and it's hard to tell which token should we blame, but sometimes it can be calculated.
         * Anyway, the token with error is reported by the pos argument. If you don't pass it, it means the error is at last token.
         *
         * Lemon parser uses it for example when there's a statement <tt>"CREATE TABLE ... (...) WITHOUT ROWID"</tt>. The SQLite grammar
         * rule says, that the <tt>"ROWID"</tt> at the end is not necessarily the <tt>ROWID</tt> keyword, but it can be any word,
         * but for now SQLite doesn't understand any other words at that position anyway and returns errors.
         */
        void errorAtToken(const QString& text, int pos = -1);

        /**
         * @brief Flushes pending errors.
         *
         * In case the errorBeforeNextToken() was called and no more tokens were feed to the context, then this method flushes
         * pending error as the error for the last token consumed, but only if minor errors are not ignored.
         * This happens for example for "SELECT " statement, where it's not correct, but it's a minor error, cause user
         * might enter more contents afterwards.
         */
        void flushErrors();

        /**
         * @brief Translates token pointer to it's shared pointer instance.
         * @param token Token pointer to translate.
         * @return QSharedPointer for the token, or null shared pointer in case of failure.
         *
         * This method works basing on internal collection of managed tokens. At each step of parsing, the internal lexer
         * provides token (in form of shared pointer) and that token is then passed to the Lemon parser (as a pure C++ pointer,
         * extracted from shared pointer). The very same token is stored in the internal collection of managed tokens (as a shared pointer).
         * This method allows to get back to the shared pointer.
         *
         * This method is necessary to use shared pointers together with Lemon parser, which works on unions and won't be able to use
         * shared pointers.
         */
        TokenPtr getTokenPtr(Token* token);

        /**
         * @brief Translates list of token pointers to their shared pointer instances.
         * @param tokens Token pointers to translate.
         * @return List of QSharedPointers.
         *
         * This method is just a convenience way to call getTokenPtr() for a list of pointers.
         */
        TokenList getTokenPtrList(const QList<Token*>& tokens);

        /**
         * @brief Adds token to managed list.
         * @param token Token to be added to managed tokens.
         * Tokens managed by context are shared to the Parser, so the API allows to see all parsed tokens.
         * Some tokens might be created outside of Lexer, so this is the central repository for all tokens to be shared.
         */
        void addManagedToken(TokenPtr token);

        /**
         * @brief Tests whether the token is in the collection of tokens managed by this context.
         * @param token Token to test.
         * @return true if the token is managed by this context, or false if not.
         */
        bool isManagedToken(Token* token);

        /**
         * @brief Provides complete list of tokens managed by this context.
         * @return List of tokens.
         */
        TokenList getManagedTokens();

        /**
         * @brief Tests whether there were any critical errors so far during parsing.
         * @return true if there were no critical errors, or false otherwise.
         */
        bool isSuccessful() const;

        /**
         * @brief Provides access to list of queries parsed so far.
         * @return List of parsed AST objects.
         *
         * If there was an error, then queries from the list might be incomplete, which means their data members
         * may still be initialized with their default values. It depends on where the error appeared in the parsed query string.
         */
        const QList<SqliteQueryPtr>& getQueries();

        /**
         * @brief Provides access to all errors occurred so far.
         * @return List of errors.
         */
        const QList<ParserError*>& getErrors();

        /**
         * @brief Flag indicating if the Lemon parser should setup token collections.
         *
         * This setting allows to define whether the Lemon parser should setup token collections for parsed AST objects.
         * In other words, it tells whether the SqliteStatement::tokens and SqliteStatement::tokensMap should be filled.
         *
         * Sometimes it might be worth to disable it to speed up parsig process, but by default it's enabled.
         */
        bool setupTokens = true;

        /**
         * @brief Flag inficating if the Lemon parser should exectute code for the grammar rules.
         *
         * This setting allows to define whether the Lemon parser should execute the code associated with rules.
         * Disabling it will cause no AST objects to be produced, but it can be used to find out syntax errors.
         * If you don't need AST objects (output from parsing), then you can turn this off to speed up Lemon parser.
         *
         * The Parser class for example turns it of when it probes for next valid token candidates. In that case
         * no AST output objects are used, just information whether the next candidate is valid or not.
         */
        bool executeRules = true;

        /**
         * @brief Flag indicating if the Lemon parser should perform "fallback" logic.
         *
         * The "fallback" login in the Lemon parser is used when the input token is one of the keywords and it failed
         * at that step. Then the "fallback" steps in and converts keyword token into the "ID" token, which represents
         * a name of any object in the database (not necessarily existing one). Then the Lemon parser retries with
         * that ID token and if that fails to fulfill the syntax rules too, then the error is reported.
         *
         * This is enabled by default, cause SQLite usually uses that too. It is for example disabled when looking
         * for the next valid token candidate in Parser::getNextTokenCandidates(), cause for that case we need
         * very strict token matching against the syntax.
         */
        bool doFallbacks = true;

        /**
         * @brief Flag indicating if minor errors should be ignored by the Lemon parser.
         *
         * See description of Parser::parse() for details.
         */
        bool ignoreMinorErrors = false;

        /**
         * @brief Dialect used for the parsing.
         *
         * This is used by the Lemon parser in various situations, like for example when it strips the object name
         * from it's wrapping characters ([], "", ``) - that depends on the dialect.
         */
        Dialect dialect;

    private:
        /**
         * @brief Clears all internal containers and deletes error objects.
         */
        void cleanUp();

        /**
         * @brief List of parsed AST objects.
         */
        QList<SqliteQueryPtr> parsedQueries;

        /**
         * @brief Tokens managed by this context.
         */
        TokenList managedTokens;

        /**
         * @brief Mapping from token pointer to it's shared pointer instance.
         */
        QHash<Token*, TokenPtr> tokenPtrMap;

        /**
         * @brief Flag indicating successful or failure parsing.
         *
         * Changed to false when the error was reported.
         */
        bool successful = true;

        /**
         * @brief List of errors reported by Lemon.
         */
        QList<ParserError*> errors;

        /**
         * @brief Flag indicating that the next token should raise an error.
         *
         * This is set by errorBeforeNextToken() and minorErrorBeforeNextToken().
         */
        bool raiseErrorBeforeNextToken = false;

        /**
         * @brief Error to be used for the error at next token.
         *
         * Defined by errorBeforeNextToken() and minorErrorBeforeNextToken().
         */
        QString nextTokenError;
};

#endif // PARSERCONTEXT_H