1 | #pragma once |
2 | |
3 | #include <set> |
4 | #include <memory> |
5 | |
6 | #include <Core/Defines.h> |
7 | #include <Core/Types.h> |
8 | #include <Parsers/IAST.h> |
9 | #include <Parsers/TokenIterator.h> |
10 | |
11 | |
12 | namespace DB |
13 | { |
14 | |
15 | namespace ErrorCodes |
16 | { |
17 | extern const int TOO_DEEP_RECURSION; |
18 | extern const int LOGICAL_ERROR; |
19 | } |
20 | |
21 | |
22 | /** Collects variants, how parser could proceed further at rightmost position. |
23 | */ |
24 | struct Expected |
25 | { |
26 | const char * max_parsed_pos = nullptr; |
27 | std::set<const char *> variants; |
28 | |
29 | /// 'description' should be statically allocated string. |
30 | void add(const char * current_pos, const char * description) |
31 | { |
32 | if (!max_parsed_pos || current_pos > max_parsed_pos) |
33 | { |
34 | variants.clear(); |
35 | max_parsed_pos = current_pos; |
36 | } |
37 | |
38 | if (!max_parsed_pos || current_pos >= max_parsed_pos) |
39 | variants.insert(description); |
40 | } |
41 | |
42 | void add(TokenIterator it, const char * description) |
43 | { |
44 | add(it->begin, description); |
45 | } |
46 | }; |
47 | |
48 | |
49 | /** Interface for parser classes |
50 | */ |
51 | class IParser |
52 | { |
53 | public: |
54 | /// Token iterator augmented with depth information. This allows to control recursion depth. |
55 | struct Pos : TokenIterator |
56 | { |
57 | using TokenIterator::TokenIterator; |
58 | |
59 | uint32_t depth = 0; |
60 | uint32_t max_depth = 1000; |
61 | |
62 | void increaseDepth() |
63 | { |
64 | ++depth; |
65 | if (depth > max_depth) |
66 | throw Exception("Maximum parse depth exceeded" , ErrorCodes::TOO_DEEP_RECURSION); |
67 | } |
68 | |
69 | void decreaseDepth() |
70 | { |
71 | if (depth == 0) |
72 | throw Exception("Logical error in parser: incorrect calculation of parse depth" , ErrorCodes::LOGICAL_ERROR); |
73 | --depth; |
74 | } |
75 | }; |
76 | |
77 | /** Get the text of this parser parses. */ |
78 | virtual const char * getName() const = 0; |
79 | |
80 | /** Parse piece of text from position `pos`, but not beyond end of line (`end` - position after end of line), |
81 | * move pointer `pos` to the maximum position to which it was possible to parse, |
82 | * in case of success return `true` and the result in `node` if it is needed, otherwise false, |
83 | * in `expected` write what was expected in the maximum position, |
84 | * to which it was possible to parse if parsing was unsuccessful, |
85 | * or what this parser parse if parsing was successful. |
86 | * The string to which the [begin, end) range is included may be not 0-terminated. |
87 | */ |
88 | virtual bool parse(Pos & pos, ASTPtr & node, Expected & expected) = 0; |
89 | |
90 | bool ignore(Pos & pos, Expected & expected) |
91 | { |
92 | ASTPtr ignore_node; |
93 | return parse(pos, ignore_node, expected); |
94 | } |
95 | |
96 | bool ignore(Pos & pos) |
97 | { |
98 | Expected expected; |
99 | return ignore(pos, expected); |
100 | } |
101 | |
102 | /** The same, but do not move the position and do not write the result to node. |
103 | */ |
104 | bool check(Pos & pos, Expected & expected) |
105 | { |
106 | Pos begin = pos; |
107 | ASTPtr node; |
108 | if (!parse(pos, node, expected)) |
109 | { |
110 | pos = begin; |
111 | return false; |
112 | } |
113 | else |
114 | return true; |
115 | } |
116 | |
117 | /** The same, but doesn't move the position even if parsing was successful. |
118 | */ |
119 | bool checkWithoutMoving(Pos pos, Expected & expected) |
120 | { |
121 | ASTPtr node; |
122 | return parse(pos, node, expected); |
123 | } |
124 | |
125 | virtual ~IParser() {} |
126 | }; |
127 | |
128 | using ParserPtr = std::unique_ptr<IParser>; |
129 | |
130 | } |
131 | |