hogql-parser 1.2.12__tar.gz → 1.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/PKG-INFO +3 -2
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/hogql_parser-stubs/__init__.pyi +35 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/hogql_parser.egg-info/PKG-INFO +3 -2
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/hogql_parser.egg-info/SOURCES.txt +2 -1
- hogql_parser-1.3.2/json.cpp +291 -0
- hogql_parser-1.2.12/parser.cpp → hogql_parser-1.3.2/parser_python.cpp +225 -189
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/setup.py +12 -9
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/HogQLLexer.cpp +0 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/HogQLParser.cpp +0 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/HogQLParserBaseVisitor.cpp +0 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/HogQLParserVisitor.cpp +0 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/README.md +0 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/error.cpp +0 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/hogql_parser.egg-info/dependency_links.txt +0 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/hogql_parser.egg-info/top_level.txt +0 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/pyproject.toml +0 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/setup.cfg +0 -0
- {hogql_parser-1.2.12 → hogql_parser-1.3.2}/string.cpp +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hogql_parser
|
|
3
|
-
Version: 1.2
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: HogQL parser for internal PostHog use
|
|
5
5
|
Home-page: https://github.com/PostHog/posthog/tree/master/common/hogql_parser
|
|
6
6
|
Author: PostHog Inc.
|
|
7
7
|
Author-email: hey@posthog.com
|
|
8
8
|
Maintainer: PostHog Inc.
|
|
9
9
|
Maintainer-email: hey@posthog.com
|
|
10
|
+
License: MIT
|
|
10
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Operating System :: MacOS
|
|
13
13
|
Classifier: Operating System :: POSIX :: Linux
|
|
14
14
|
Classifier: Programming Language :: Python
|
|
@@ -23,6 +23,7 @@ Dynamic: classifier
|
|
|
23
23
|
Dynamic: description
|
|
24
24
|
Dynamic: description-content-type
|
|
25
25
|
Dynamic: home-page
|
|
26
|
+
Dynamic: license
|
|
26
27
|
Dynamic: maintainer
|
|
27
28
|
Dynamic: maintainer-email
|
|
28
29
|
Dynamic: requires-python
|
|
@@ -42,3 +42,38 @@ def parse_program(source: str, /, *, is_internal: bool = False) -> Program:
|
|
|
42
42
|
If the expr `is_internal`, spans and notices won't be included in the AST.
|
|
43
43
|
"""
|
|
44
44
|
...
|
|
45
|
+
|
|
46
|
+
def parse_expr_json(expr: str, /, *, is_internal: bool = False) -> str:
|
|
47
|
+
"""Parse the HogQL expression string into a JSON AST.
|
|
48
|
+
|
|
49
|
+
If the expr `is_internal`, spans and notices won't be included in the AST.
|
|
50
|
+
"""
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
def parse_order_expr_json(expr: str, /, *, is_internal: bool = False) -> str:
|
|
54
|
+
"""Parse the ORDER BY clause string into a JSON AST.
|
|
55
|
+
|
|
56
|
+
If the expr `is_internal`, spans and notices won't be included in the AST.
|
|
57
|
+
"""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
def parse_select_json(expr: str, /, *, is_internal: bool = False) -> str:
|
|
61
|
+
"""Parse the HogQL SELECT statement string into a JSON AST.
|
|
62
|
+
|
|
63
|
+
If the expr `is_internal`, spans and notices won't be included in the AST.
|
|
64
|
+
"""
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
def parse_full_template_string_json(expr: str, /, *, is_internal: bool = False) -> str:
|
|
68
|
+
"""Parse a Hog template string into a JSON AST.
|
|
69
|
+
|
|
70
|
+
If the expr `is_internal`, spans and notices won't be included in the AST.
|
|
71
|
+
"""
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
def parse_program_json(source: str, /, *, is_internal: bool = False) -> str:
|
|
75
|
+
"""Parse a Hog program into a JSON AST.
|
|
76
|
+
|
|
77
|
+
If the expr `is_internal`, spans and notices won't be included in the AST.
|
|
78
|
+
"""
|
|
79
|
+
...
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hogql_parser
|
|
3
|
-
Version: 1.2
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: HogQL parser for internal PostHog use
|
|
5
5
|
Home-page: https://github.com/PostHog/posthog/tree/master/common/hogql_parser
|
|
6
6
|
Author: PostHog Inc.
|
|
7
7
|
Author-email: hey@posthog.com
|
|
8
8
|
Maintainer: PostHog Inc.
|
|
9
9
|
Maintainer-email: hey@posthog.com
|
|
10
|
+
License: MIT
|
|
10
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Operating System :: MacOS
|
|
13
13
|
Classifier: Operating System :: POSIX :: Linux
|
|
14
14
|
Classifier: Programming Language :: Python
|
|
@@ -23,6 +23,7 @@ Dynamic: classifier
|
|
|
23
23
|
Dynamic: description
|
|
24
24
|
Dynamic: description-content-type
|
|
25
25
|
Dynamic: home-page
|
|
26
|
+
Dynamic: license
|
|
26
27
|
Dynamic: maintainer
|
|
27
28
|
Dynamic: maintainer-email
|
|
28
29
|
Dynamic: requires-python
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#include "json.h"
|
|
2
|
+
|
|
3
|
+
#include <iomanip>
|
|
4
|
+
#include <sstream>
|
|
5
|
+
#include <stdexcept>
|
|
6
|
+
|
|
7
|
+
// Static empty instances for safe returns
|
|
8
|
+
static const Json::String emptyString;
|
|
9
|
+
static const Json::Array emptyArray;
|
|
10
|
+
static const Json::Object emptyObject;
|
|
11
|
+
|
|
12
|
+
// Initializer list constructor - creates array
|
|
13
|
+
Json::Json(std::initializer_list<Json> init) : value(Array(init)) {}
|
|
14
|
+
|
|
15
|
+
// Object subscript operator
|
|
16
|
+
Json& Json::operator[](const std::string& key) {
|
|
17
|
+
if (isNull()) {
|
|
18
|
+
value = Object{};
|
|
19
|
+
}
|
|
20
|
+
if (!isObject()) {
|
|
21
|
+
throw std::runtime_error("Json::operator[](string): not an object");
|
|
22
|
+
}
|
|
23
|
+
return std::get<Object>(value)[key];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Array subscript operator
|
|
27
|
+
Json& Json::operator[](size_t index) {
|
|
28
|
+
if (isNull()) {
|
|
29
|
+
value = Array{};
|
|
30
|
+
}
|
|
31
|
+
if (!isArray()) {
|
|
32
|
+
throw std::runtime_error("Json::operator[](size_t): not an array");
|
|
33
|
+
}
|
|
34
|
+
auto& arr = std::get<Array>(value);
|
|
35
|
+
if (index >= arr.size()) {
|
|
36
|
+
arr.resize(index + 1);
|
|
37
|
+
}
|
|
38
|
+
return arr[index];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Value getters
|
|
42
|
+
Json::Bool Json::getBool(Bool defaultVal) const {
|
|
43
|
+
if (isBool()) {
|
|
44
|
+
return std::get<Bool>(value);
|
|
45
|
+
}
|
|
46
|
+
return defaultVal;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Json::Int Json::getInt(Int defaultVal) const {
|
|
50
|
+
if (isInt()) {
|
|
51
|
+
return std::get<Int>(value);
|
|
52
|
+
}
|
|
53
|
+
if (isFloat()) {
|
|
54
|
+
return static_cast<Int>(std::get<Float>(value));
|
|
55
|
+
}
|
|
56
|
+
return defaultVal;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Json::Float Json::getFloat(Float defaultVal) const {
|
|
60
|
+
if (isFloat()) {
|
|
61
|
+
return std::get<Float>(value);
|
|
62
|
+
}
|
|
63
|
+
if (isInt()) {
|
|
64
|
+
return static_cast<Float>(std::get<Int>(value));
|
|
65
|
+
}
|
|
66
|
+
return defaultVal;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const Json::String& Json::getString() const {
|
|
70
|
+
if (isString()) {
|
|
71
|
+
return std::get<String>(value);
|
|
72
|
+
}
|
|
73
|
+
return emptyString;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const Json::Array& Json::getArray() const {
|
|
77
|
+
if (isArray()) {
|
|
78
|
+
return std::get<Array>(value);
|
|
79
|
+
}
|
|
80
|
+
return emptyArray;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const Json::Object& Json::getObject() const {
|
|
84
|
+
if (isObject()) {
|
|
85
|
+
return std::get<Object>(value);
|
|
86
|
+
}
|
|
87
|
+
return emptyObject;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
Json::Array& Json::getArrayMut() {
|
|
91
|
+
if (isNull()) {
|
|
92
|
+
value = Array{};
|
|
93
|
+
}
|
|
94
|
+
if (!isArray()) {
|
|
95
|
+
throw std::runtime_error("Json::getArrayMut(): not an array");
|
|
96
|
+
}
|
|
97
|
+
return std::get<Array>(value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Json::Object& Json::getObjectMut() {
|
|
101
|
+
if (isNull()) {
|
|
102
|
+
value = Object{};
|
|
103
|
+
}
|
|
104
|
+
if (!isObject()) {
|
|
105
|
+
throw std::runtime_error("Json::getObjectMut(): not an object");
|
|
106
|
+
}
|
|
107
|
+
return std::get<Object>(value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Array operations
|
|
111
|
+
void Json::pushBack(const Json& val) {
|
|
112
|
+
if (isNull()) {
|
|
113
|
+
value = Array{};
|
|
114
|
+
}
|
|
115
|
+
if (!isArray()) {
|
|
116
|
+
throw std::runtime_error("Json::pushBack(): not an array");
|
|
117
|
+
}
|
|
118
|
+
std::get<Array>(value).push_back(val);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
void Json::pushBack(Json&& val) {
|
|
122
|
+
if (isNull()) {
|
|
123
|
+
value = Array{};
|
|
124
|
+
}
|
|
125
|
+
if (!isArray()) {
|
|
126
|
+
throw std::runtime_error("Json::pushBack(): not an array");
|
|
127
|
+
}
|
|
128
|
+
std::get<Array>(value).push_back(std::move(val));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
size_t Json::size() const {
|
|
132
|
+
if (isArray()) {
|
|
133
|
+
return std::get<Array>(value).size();
|
|
134
|
+
}
|
|
135
|
+
if (isObject()) {
|
|
136
|
+
return std::get<Object>(value).size();
|
|
137
|
+
}
|
|
138
|
+
if (isString()) {
|
|
139
|
+
return std::get<String>(value).size();
|
|
140
|
+
}
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
bool Json::empty() const {
|
|
145
|
+
if (isArray()) {
|
|
146
|
+
return std::get<Array>(value).empty();
|
|
147
|
+
}
|
|
148
|
+
if (isObject()) {
|
|
149
|
+
return std::get<Object>(value).empty();
|
|
150
|
+
}
|
|
151
|
+
if (isString()) {
|
|
152
|
+
return std::get<String>(value).empty();
|
|
153
|
+
}
|
|
154
|
+
return isNull();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// String escaping for JSON output
|
|
158
|
+
std::string Json::escapeString(const std::string& s) {
|
|
159
|
+
std::ostringstream escaped;
|
|
160
|
+
escaped << '"';
|
|
161
|
+
for (char c : s) {
|
|
162
|
+
switch (c) {
|
|
163
|
+
case '"':
|
|
164
|
+
escaped << "\\\"";
|
|
165
|
+
break;
|
|
166
|
+
case '\\':
|
|
167
|
+
escaped << "\\\\";
|
|
168
|
+
break;
|
|
169
|
+
case '\b':
|
|
170
|
+
escaped << "\\b";
|
|
171
|
+
break;
|
|
172
|
+
case '\f':
|
|
173
|
+
escaped << "\\f";
|
|
174
|
+
break;
|
|
175
|
+
case '\n':
|
|
176
|
+
escaped << "\\n";
|
|
177
|
+
break;
|
|
178
|
+
case '\r':
|
|
179
|
+
escaped << "\\r";
|
|
180
|
+
break;
|
|
181
|
+
case '\t':
|
|
182
|
+
escaped << "\\t";
|
|
183
|
+
break;
|
|
184
|
+
default:
|
|
185
|
+
if (static_cast<unsigned char>(c) < 0x20) {
|
|
186
|
+
escaped << "\\u" << std::hex << std::setw(4) << std::setfill('0')
|
|
187
|
+
<< static_cast<int>(static_cast<unsigned char>(c));
|
|
188
|
+
} else {
|
|
189
|
+
escaped << c;
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
escaped << '"';
|
|
195
|
+
return escaped.str();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
std::string Json::indentString(int spaces) {
|
|
199
|
+
if (spaces <= 0) {
|
|
200
|
+
return "";
|
|
201
|
+
}
|
|
202
|
+
return std::string(spaces, ' ');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
std::string Json::dump(int indent) const {
|
|
206
|
+
return dumpImpl(indent, 0);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
std::string Json::dumpImpl(int indent, int currentIndent) const {
|
|
210
|
+
return std::visit(
|
|
211
|
+
[this, indent, currentIndent](const auto& v) -> std::string {
|
|
212
|
+
using T = std::decay_t<decltype(v)>;
|
|
213
|
+
|
|
214
|
+
if constexpr (std::is_same_v<T, Null>) {
|
|
215
|
+
return "null";
|
|
216
|
+
} else if constexpr (std::is_same_v<T, Bool>) {
|
|
217
|
+
return v ? "true" : "false";
|
|
218
|
+
} else if constexpr (std::is_same_v<T, Int>) {
|
|
219
|
+
return std::to_string(v);
|
|
220
|
+
} else if constexpr (std::is_same_v<T, Float>) {
|
|
221
|
+
std::ostringstream oss;
|
|
222
|
+
oss << std::setprecision(17) << v;
|
|
223
|
+
std::string result = oss.str();
|
|
224
|
+
// Ensure it looks like a float (has decimal point or exponent)
|
|
225
|
+
if (result.find('.') == std::string::npos && result.find('e') == std::string::npos &&
|
|
226
|
+
result.find('E') == std::string::npos) {
|
|
227
|
+
result += ".0";
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
} else if constexpr (std::is_same_v<T, String>) {
|
|
231
|
+
return escapeString(v);
|
|
232
|
+
} else if constexpr (std::is_same_v<T, Raw>) {
|
|
233
|
+
// Raw JSON is already serialized, output as-is
|
|
234
|
+
return v.json;
|
|
235
|
+
} else if constexpr (std::is_same_v<T, Array>) {
|
|
236
|
+
if (v.empty()) {
|
|
237
|
+
return "[]";
|
|
238
|
+
}
|
|
239
|
+
std::ostringstream oss;
|
|
240
|
+
oss << '[';
|
|
241
|
+
bool pretty = indent >= 0;
|
|
242
|
+
int nextIndent = currentIndent + (pretty ? indent : 0);
|
|
243
|
+
|
|
244
|
+
for (size_t i = 0; i < v.size(); ++i) {
|
|
245
|
+
if (pretty) {
|
|
246
|
+
oss << '\n' << indentString(nextIndent);
|
|
247
|
+
}
|
|
248
|
+
oss << v[i].dumpImpl(indent, nextIndent);
|
|
249
|
+
if (i < v.size() - 1) {
|
|
250
|
+
oss << ',';
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (pretty) {
|
|
254
|
+
oss << '\n' << indentString(currentIndent);
|
|
255
|
+
}
|
|
256
|
+
oss << ']';
|
|
257
|
+
return oss.str();
|
|
258
|
+
} else if constexpr (std::is_same_v<T, Object>) {
|
|
259
|
+
if (v.empty()) {
|
|
260
|
+
return "{}";
|
|
261
|
+
}
|
|
262
|
+
std::ostringstream oss;
|
|
263
|
+
oss << '{';
|
|
264
|
+
bool pretty = indent >= 0;
|
|
265
|
+
int nextIndent = currentIndent + (pretty ? indent : 0);
|
|
266
|
+
|
|
267
|
+
size_t i = 0;
|
|
268
|
+
for (const auto& [key, val] : v) {
|
|
269
|
+
if (pretty) {
|
|
270
|
+
oss << '\n' << indentString(nextIndent);
|
|
271
|
+
}
|
|
272
|
+
oss << escapeString(key) << ':';
|
|
273
|
+
if (pretty) {
|
|
274
|
+
oss << ' ';
|
|
275
|
+
}
|
|
276
|
+
oss << val.dumpImpl(indent, nextIndent);
|
|
277
|
+
if (i < v.size() - 1) {
|
|
278
|
+
oss << ',';
|
|
279
|
+
}
|
|
280
|
+
++i;
|
|
281
|
+
}
|
|
282
|
+
if (pretty) {
|
|
283
|
+
oss << '\n' << indentString(currentIndent);
|
|
284
|
+
}
|
|
285
|
+
oss << '}';
|
|
286
|
+
return oss.str();
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
value
|
|
290
|
+
);
|
|
291
|
+
}
|
|
@@ -8,16 +8,16 @@
|
|
|
8
8
|
#include "HogQLParserBaseVisitor.h"
|
|
9
9
|
|
|
10
10
|
#include "error.h"
|
|
11
|
-
#include "
|
|
11
|
+
#include "parser_python.h"
|
|
12
12
|
#include "string.h"
|
|
13
13
|
|
|
14
14
|
#define VISIT(RULE) any visit##RULE(HogQLParser::RULE##Context* ctx) override
|
|
15
|
-
#define VISIT_UNSUPPORTED(RULE)
|
|
16
|
-
VISIT(RULE) {
|
|
15
|
+
#define VISIT_UNSUPPORTED(RULE) \
|
|
16
|
+
VISIT(RULE) { \
|
|
17
17
|
throw NotImplementedError("Unsupported rule: " #RULE); \
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
#define HANDLE_HOGQL_ERROR(TYPE, OTHER_CLEANUP)
|
|
20
|
+
#define HANDLE_HOGQL_ERROR(TYPE, OTHER_CLEANUP) \
|
|
21
21
|
(const TYPE& e) { \
|
|
22
22
|
string err_what = e.what(); \
|
|
23
23
|
PyObject *error_type = NULL, *py_err_args = NULL, *py_err = NULL, *py_start = NULL, *py_end = NULL; \
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
PyObject* ret = build_ast_node(TYPE_NAME, KWARGS_FORMAT, __VA_ARGS__); \
|
|
51
51
|
/* Fortunately we don't need to care about decrementing Py_BuildValue/Py_VaBuildValue args, */ \
|
|
52
52
|
/* so just throw is enough: https://github.com/python/cpython/blob/a254120f/Python/modsupport.c#L147-L148*/ \
|
|
53
|
-
if (!ret) throw PyInternalError();
|
|
53
|
+
if (!ret) throw PyInternalError(); \
|
|
54
54
|
return ret
|
|
55
55
|
|
|
56
56
|
using namespace std;
|
|
@@ -221,9 +221,9 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
221
221
|
PyObject* visitAsPyObjectFinal(antlr4::tree::ParseTree* tree) {
|
|
222
222
|
try {
|
|
223
223
|
return visitAsPyObject(tree);
|
|
224
|
-
} catch HANDLE_HOGQL_ERROR(SyntaxError, ) catch HANDLE_HOGQL_ERROR(
|
|
225
|
-
|
|
226
|
-
) catch
|
|
224
|
+
} catch HANDLE_HOGQL_ERROR(SyntaxError, ) catch HANDLE_HOGQL_ERROR(NotImplementedError, ) catch HANDLE_HOGQL_ERROR(
|
|
225
|
+
ParsingError,
|
|
226
|
+
) catch (const PyInternalError& e) {
|
|
227
227
|
return NULL;
|
|
228
228
|
} catch (const bad_any_cast& e) {
|
|
229
229
|
PyObject* error_type = PyObject_GetAttrString(state->errors_module, "ParsingError");
|
|
@@ -334,9 +334,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
334
334
|
throw ParsingError("Declaration must be either a varDecl or a statement");
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
-
VISIT(Expression) {
|
|
338
|
-
return visit(ctx->columnExpr());
|
|
339
|
-
}
|
|
337
|
+
VISIT(Expression) { return visit(ctx->columnExpr()); }
|
|
340
338
|
|
|
341
339
|
VISIT(VarDecl) {
|
|
342
340
|
string name = visitAsString(ctx->identifier());
|
|
@@ -428,8 +426,11 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
428
426
|
return visit(empty_stmt_ctx);
|
|
429
427
|
}
|
|
430
428
|
|
|
431
|
-
throw ParsingError(
|
|
432
|
-
|
|
429
|
+
throw ParsingError(
|
|
430
|
+
"Statement must be one of returnStmt, throwStmt, tryCatchStmt, ifStmt, whileStmt, forStmt, forInStmt, "
|
|
431
|
+
"funcStmt, "
|
|
432
|
+
"varAssignment, block, exprStmt, or emptyStmt"
|
|
433
|
+
);
|
|
433
434
|
}
|
|
434
435
|
|
|
435
436
|
VISIT(ExprStmt) {
|
|
@@ -707,21 +708,16 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
707
708
|
Py_DECREF(expr);
|
|
708
709
|
throw;
|
|
709
710
|
}
|
|
710
|
-
PyObject* ret =
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
"keyVar", first_identifier.data(), first_identifier.size(),
|
|
721
|
-
"valueVar", second_identifier.data(), second_identifier.size(),
|
|
722
|
-
"expr", expr,
|
|
723
|
-
"body", body
|
|
724
|
-
);
|
|
711
|
+
PyObject* ret =
|
|
712
|
+
second_identifier.empty()
|
|
713
|
+
? build_ast_node(
|
|
714
|
+
"ForInStatement", "{s:O,s:s#,s:N,s:N}", "keyVar", Py_None, "valueVar", first_identifier.data(),
|
|
715
|
+
first_identifier.size(), "expr", expr, "body", body
|
|
716
|
+
)
|
|
717
|
+
: build_ast_node(
|
|
718
|
+
"ForInStatement", "{s:s#,s:s#,s:N,s:N}", "keyVar", first_identifier.data(), first_identifier.size(),
|
|
719
|
+
"valueVar", second_identifier.data(), second_identifier.size(), "expr", expr, "body", body
|
|
720
|
+
);
|
|
725
721
|
if (!ret) {
|
|
726
722
|
Py_DECREF(expr);
|
|
727
723
|
Py_DECREF(body);
|
|
@@ -743,7 +739,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
743
739
|
}
|
|
744
740
|
|
|
745
741
|
if (!params) {
|
|
746
|
-
|
|
742
|
+
throw PyInternalError();
|
|
747
743
|
}
|
|
748
744
|
|
|
749
745
|
PyObject* body;
|
|
@@ -754,7 +750,8 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
754
750
|
throw;
|
|
755
751
|
}
|
|
756
752
|
|
|
757
|
-
PyObject* ret =
|
|
753
|
+
PyObject* ret =
|
|
754
|
+
build_ast_node("Function", "{s:s#,s:N,s:N}", "name", name.data(), name.size(), "params", params, "body", body);
|
|
758
755
|
if (!ret) {
|
|
759
756
|
Py_DECREF(params);
|
|
760
757
|
Py_DECREF(body);
|
|
@@ -763,9 +760,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
763
760
|
return ret;
|
|
764
761
|
}
|
|
765
762
|
|
|
766
|
-
VISIT(KvPairList) {
|
|
767
|
-
return visitPyListOfObjects(ctx->kvPair());
|
|
768
|
-
}
|
|
763
|
+
VISIT(KvPairList) { return visitPyListOfObjects(ctx->kvPair()); }
|
|
769
764
|
|
|
770
765
|
VISIT(KvPair) {
|
|
771
766
|
PyObject* k = visitAsPyObject(ctx->expression(0));
|
|
@@ -785,13 +780,9 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
785
780
|
return ret;
|
|
786
781
|
}
|
|
787
782
|
|
|
788
|
-
VISIT(IdentifierList) {
|
|
789
|
-
return visitAsVectorOfStrings(ctx->identifier());
|
|
790
|
-
}
|
|
783
|
+
VISIT(IdentifierList) { return visitAsVectorOfStrings(ctx->identifier()); }
|
|
791
784
|
|
|
792
|
-
VISIT(EmptyStmt) {
|
|
793
|
-
RETURN_NEW_AST_NODE("ExprStatement", "{s:O}", "expr", Py_None);
|
|
794
|
-
}
|
|
785
|
+
VISIT(EmptyStmt) { RETURN_NEW_AST_NODE("ExprStatement", "{s:O}", "expr", Py_None); }
|
|
795
786
|
|
|
796
787
|
VISIT(Block) {
|
|
797
788
|
PyObject* declarations = PyList_New(0);
|
|
@@ -866,29 +857,34 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
866
857
|
for (auto subsequent : ctx->subsequentSelectSetClause()) {
|
|
867
858
|
char* set_operator;
|
|
868
859
|
if (subsequent->UNION() && subsequent->ALL()) {
|
|
869
|
-
|
|
860
|
+
set_operator = "UNION ALL";
|
|
870
861
|
} else if (subsequent->UNION() && subsequent->DISTINCT()) {
|
|
871
|
-
|
|
862
|
+
set_operator = "UNION DISTINCT";
|
|
872
863
|
} else if (subsequent->INTERSECT() && subsequent->DISTINCT()) {
|
|
873
|
-
|
|
864
|
+
set_operator = "INTERSECT DISTINCT";
|
|
874
865
|
} else if (subsequent->INTERSECT()) {
|
|
875
|
-
|
|
866
|
+
set_operator = "INTERSECT";
|
|
876
867
|
} else if (subsequent->EXCEPT()) {
|
|
877
|
-
|
|
868
|
+
set_operator = "EXCEPT";
|
|
878
869
|
} else {
|
|
879
|
-
|
|
870
|
+
throw SyntaxError(
|
|
871
|
+
"Set operator must be one of UNION ALL, UNION DISTINCT, INTERSECT, INTERSECT DISTINCT, and EXCEPT"
|
|
872
|
+
);
|
|
880
873
|
}
|
|
881
874
|
select_query = visitAsPyObject(subsequent->selectStmtWithParens());
|
|
882
|
-
PyObject* query = build_ast_node(
|
|
875
|
+
PyObject* query = build_ast_node(
|
|
876
|
+
"SelectSetNode", "{s:N,s:N}", "select_query", select_query, "set_operator",
|
|
877
|
+
PyUnicode_FromString(set_operator)
|
|
878
|
+
);
|
|
883
879
|
if (!query) {
|
|
884
880
|
throw PyInternalError();
|
|
885
881
|
}
|
|
886
882
|
PyList_Append(select_queries, query);
|
|
887
883
|
}
|
|
888
884
|
} catch (...) {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
885
|
+
Py_DECREF(select_queries);
|
|
886
|
+
Py_DECREF(initial_query);
|
|
887
|
+
throw;
|
|
892
888
|
}
|
|
893
889
|
|
|
894
890
|
if (PyList_Size(select_queries) == 0) {
|
|
@@ -896,7 +892,10 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
896
892
|
return initial_query;
|
|
897
893
|
}
|
|
898
894
|
|
|
899
|
-
RETURN_NEW_AST_NODE(
|
|
895
|
+
RETURN_NEW_AST_NODE(
|
|
896
|
+
"SelectSetQuery", "{s:N, s:N}", "initial_select_query", initial_query, "subsequent_select_queries",
|
|
897
|
+
select_queries
|
|
898
|
+
);
|
|
900
899
|
}
|
|
901
900
|
|
|
902
901
|
VISIT(SelectStmt) {
|
|
@@ -1182,10 +1181,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
1182
1181
|
}
|
|
1183
1182
|
|
|
1184
1183
|
// Create the LimitByExpr node (transfers ownership of n, offset_value, and exprs)
|
|
1185
|
-
RETURN_NEW_AST_NODE("LimitByExpr", "{s:N,s:N,s:N}",
|
|
1186
|
-
"n", n,
|
|
1187
|
-
"offset_value", offset_value,
|
|
1188
|
-
"exprs", exprs);
|
|
1184
|
+
RETURN_NEW_AST_NODE("LimitByExpr", "{s:N,s:N,s:N}", "n", n, "offset_value", offset_value, "exprs", exprs);
|
|
1189
1185
|
}
|
|
1190
1186
|
|
|
1191
1187
|
VISIT(LimitExpr) {
|
|
@@ -1196,7 +1192,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
1196
1192
|
|
|
1197
1193
|
// If no second expression, just return the first
|
|
1198
1194
|
if (!ctx->columnExpr(1)) {
|
|
1199
|
-
return first;
|
|
1195
|
+
return first; // Return first as is, ownership transferred
|
|
1200
1196
|
}
|
|
1201
1197
|
|
|
1202
1198
|
// We have both limit and offset
|
|
@@ -1227,13 +1223,11 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
1227
1223
|
return result;
|
|
1228
1224
|
}
|
|
1229
1225
|
|
|
1230
|
-
VISIT(OffsetOnlyClause) {
|
|
1231
|
-
return visitAsPyObject(ctx->columnExpr());
|
|
1232
|
-
}
|
|
1226
|
+
VISIT(OffsetOnlyClause) { return visitAsPyObject(ctx->columnExpr()); }
|
|
1233
1227
|
|
|
1234
1228
|
VISIT_UNSUPPORTED(ProjectionOrderByClause)
|
|
1235
1229
|
|
|
1236
|
-
VISIT_UNSUPPORTED(LimitAndOffsetClause)
|
|
1230
|
+
VISIT_UNSUPPORTED(LimitAndOffsetClause) // We handle this directly in the SelectStmt visitor
|
|
1237
1231
|
|
|
1238
1232
|
VISIT_UNSUPPORTED(SettingsClause)
|
|
1239
1233
|
|
|
@@ -1246,20 +1240,20 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
1246
1240
|
if (!next_join) { \
|
|
1247
1241
|
Py_DECREF(join1); \
|
|
1248
1242
|
Py_DECREF(join2); \
|
|
1249
|
-
throw PyInternalError();
|
|
1243
|
+
throw PyInternalError(); \
|
|
1250
1244
|
} \
|
|
1251
1245
|
int reached_end_of_chain = Py_IsNone(next_join); \
|
|
1252
1246
|
if (reached_end_of_chain == -1) { \
|
|
1253
1247
|
Py_DECREF(join1); \
|
|
1254
1248
|
Py_DECREF(join2); \
|
|
1255
|
-
throw PyInternalError();
|
|
1249
|
+
throw PyInternalError(); \
|
|
1256
1250
|
} \
|
|
1257
1251
|
if (reached_end_of_chain) { \
|
|
1258
1252
|
int err_indicator = PyObject_SetAttrString(last_join, "next_join", join2); \
|
|
1259
1253
|
if (err_indicator == -1) { \
|
|
1260
1254
|
Py_DECREF(join1); \
|
|
1261
1255
|
Py_DECREF(join2); \
|
|
1262
|
-
throw PyInternalError();
|
|
1256
|
+
throw PyInternalError(); \
|
|
1263
1257
|
} \
|
|
1264
1258
|
Py_DECREF(join2); \
|
|
1265
1259
|
return join1; \
|
|
@@ -1781,7 +1775,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
1781
1775
|
if (is_##VAR##_a_call == -1) { \
|
|
1782
1776
|
Py_DECREF(left); \
|
|
1783
1777
|
Py_DECREF(right); \
|
|
1784
|
-
throw PyInternalError();
|
|
1778
|
+
throw PyInternalError(); \
|
|
1785
1779
|
} \
|
|
1786
1780
|
if (is_##VAR##_a_call) { \
|
|
1787
1781
|
PyObject* VAR##_name = PyObject_GetAttrString(VAR, "name"); \
|
|
@@ -1789,7 +1783,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
1789
1783
|
Py_DECREF(left); \
|
|
1790
1784
|
Py_DECREF(right); \
|
|
1791
1785
|
Py_DECREF(concat_as_str); \
|
|
1792
|
-
throw PyInternalError();
|
|
1786
|
+
throw PyInternalError(); \
|
|
1793
1787
|
} \
|
|
1794
1788
|
PyObject* VAR##_name_lower = PyObject_CallMethod(VAR##_name, "lower", NULL); \
|
|
1795
1789
|
Py_DECREF(VAR##_name); \
|
|
@@ -1797,7 +1791,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
1797
1791
|
Py_DECREF(left); \
|
|
1798
1792
|
Py_DECREF(right); \
|
|
1799
1793
|
Py_DECREF(concat_as_str); \
|
|
1800
|
-
throw PyInternalError();
|
|
1794
|
+
throw PyInternalError(); \
|
|
1801
1795
|
} \
|
|
1802
1796
|
is_##VAR##_a_concat_call = PyObject_RichCompareBool(VAR##_name_lower, concat_as_str, Py_EQ); \
|
|
1803
1797
|
Py_DECREF(VAR##_name_lower); \
|
|
@@ -1805,7 +1799,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
1805
1799
|
Py_DECREF(left); \
|
|
1806
1800
|
Py_DECREF(right); \
|
|
1807
1801
|
Py_DECREF(concat_as_str); \
|
|
1808
|
-
throw PyInternalError();
|
|
1802
|
+
throw PyInternalError(); \
|
|
1809
1803
|
} \
|
|
1810
1804
|
}
|
|
1811
1805
|
|
|
@@ -2014,10 +2008,9 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
2014
2008
|
}
|
|
2015
2009
|
|
|
2016
2010
|
PyObject* ret = build_ast_node(
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
"args", constant_count
|
|
2011
|
+
"Call",
|
|
2012
|
+
"{s:s,s:[O]}", // s:[O] means "args" => [the single PyObject]
|
|
2013
|
+
"name", name.c_str(), "args", constant_count
|
|
2021
2014
|
);
|
|
2022
2015
|
|
|
2023
2016
|
if (!ret) {
|
|
@@ -2142,12 +2135,8 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
2142
2135
|
throw;
|
|
2143
2136
|
}
|
|
2144
2137
|
RETURN_NEW_AST_NODE(
|
|
2145
|
-
"BetweenExpr",
|
|
2146
|
-
|
|
2147
|
-
"expr", expr,
|
|
2148
|
-
"low", low,
|
|
2149
|
-
"high", high,
|
|
2150
|
-
"negated", ctx->NOT() ? Py_True : Py_False
|
|
2138
|
+
"BetweenExpr", "{s:N,s:N,s:N,s:O}", "expr", expr, "low", low, "high", high, "negated",
|
|
2139
|
+
ctx->NOT() ? Py_True : Py_False
|
|
2151
2140
|
);
|
|
2152
2141
|
}
|
|
2153
2142
|
|
|
@@ -2353,8 +2342,8 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
2353
2342
|
throw;
|
|
2354
2343
|
}
|
|
2355
2344
|
RETURN_NEW_AST_NODE(
|
|
2356
|
-
"WindowFunction", "{s:s#,s:N,s:N,s:N}", "name", identifier.data(), identifier.size(), "exprs", exprs,
|
|
2357
|
-
|
|
2345
|
+
"WindowFunction", "{s:s#,s:N,s:N,s:N}", "name", identifier.data(), identifier.size(), "exprs", exprs, "args",
|
|
2346
|
+
args, "over_expr", over_expr
|
|
2358
2347
|
);
|
|
2359
2348
|
}
|
|
2360
2349
|
|
|
@@ -2687,101 +2676,116 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
2687
2676
|
}
|
|
2688
2677
|
|
|
2689
2678
|
auto attribute_ctxs = ctx->hogqlxTagAttribute();
|
|
2690
|
-
PyObject
|
|
2679
|
+
PyObject* attributes = PyList_New(attribute_ctxs.size());
|
|
2691
2680
|
if (!attributes) {
|
|
2692
2681
|
throw PyInternalError();
|
|
2693
2682
|
}
|
|
2694
2683
|
for (size_t i = 0; i < attribute_ctxs.size(); i++) {
|
|
2695
|
-
PyObject
|
|
2684
|
+
PyObject* attr_obj;
|
|
2696
2685
|
try {
|
|
2697
2686
|
attr_obj = visitAsPyObject(attribute_ctxs[i]);
|
|
2698
2687
|
} catch (...) {
|
|
2699
2688
|
Py_DECREF(attributes);
|
|
2700
2689
|
throw;
|
|
2701
2690
|
}
|
|
2702
|
-
PyList_SET_ITEM(attributes, i, attr_obj);
|
|
2691
|
+
PyList_SET_ITEM(attributes, i, attr_obj); // Steals reference
|
|
2703
2692
|
}
|
|
2704
2693
|
|
|
2705
2694
|
/* ── children ───────────────────────────────────────────── */
|
|
2706
2695
|
std::vector<PyObject*> kept_children;
|
|
2707
2696
|
for (auto childCtx : ctx->hogqlxChildElement()) {
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2697
|
+
PyObject* child_ast;
|
|
2698
|
+
try {
|
|
2699
|
+
child_ast = visitAsPyObject(childCtx);
|
|
2700
|
+
} catch (...) {
|
|
2701
|
+
X_Py_DECREF_ALL(kept_children);
|
|
2702
|
+
Py_DECREF(attributes);
|
|
2703
|
+
throw;
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
/* drop Constant nodes that are only-whitespace *and* contain a line-break */
|
|
2707
|
+
int is_const = is_ast_node_instance(child_ast, "Constant");
|
|
2708
|
+
if (is_const == 1) {
|
|
2709
|
+
PyObject* valueObj = PyObject_GetAttrString(child_ast, "value");
|
|
2710
|
+
if (valueObj && PyUnicode_Check(valueObj)) {
|
|
2711
|
+
Py_ssize_t n = 0;
|
|
2712
|
+
const char* s = PyUnicode_AsUTF8AndSize(valueObj, &n);
|
|
2713
|
+
if (s) {
|
|
2714
|
+
std::string v(s, n);
|
|
2715
|
+
bool only_ws = std::all_of(v.begin(), v.end(), [](unsigned char c) { return std::isspace(c); });
|
|
2716
|
+
bool has_newline = v.find('\n') != std::string::npos || v.find('\r') != std::string::npos;
|
|
2717
|
+
if (only_ws && has_newline) { // skip it
|
|
2718
|
+
Py_DECREF(valueObj);
|
|
2719
|
+
Py_DECREF(child_ast);
|
|
2720
|
+
continue;
|
|
2730
2721
|
}
|
|
2731
|
-
|
|
2722
|
+
}
|
|
2732
2723
|
}
|
|
2724
|
+
Py_XDECREF(valueObj);
|
|
2725
|
+
}
|
|
2733
2726
|
|
|
2734
|
-
|
|
2727
|
+
kept_children.push_back(child_ast); // keep
|
|
2735
2728
|
}
|
|
2736
2729
|
|
|
2737
2730
|
/* if we have child nodes, validate + attach them as attribute "children" */
|
|
2738
2731
|
if (!kept_children.empty()) {
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2732
|
+
for (Py_ssize_t i = 0; i < PyList_Size(attributes); ++i) {
|
|
2733
|
+
PyObject* attr = PyList_GetItem(attributes, i); // borrowed
|
|
2734
|
+
PyObject* name_obj = PyObject_GetAttrString(attr, "name");
|
|
2735
|
+
PyObject* children_str = PyUnicode_FromString("children");
|
|
2736
|
+
int is_children = PyObject_RichCompareBool(name_obj, children_str, Py_EQ);
|
|
2737
|
+
Py_DECREF(children_str);
|
|
2738
|
+
Py_DECREF(name_obj);
|
|
2739
|
+
if (is_children == 1) {
|
|
2740
|
+
X_Py_DECREF_ALL(kept_children);
|
|
2741
|
+
Py_DECREF(attributes);
|
|
2742
|
+
throw SyntaxError("Can't have a HogQLX tag with both children and a 'children' attribute");
|
|
2743
|
+
}
|
|
2744
|
+
if (is_children == -1) {
|
|
2745
|
+
X_Py_DECREF_ALL(kept_children);
|
|
2746
|
+
Py_DECREF(attributes);
|
|
2747
|
+
throw PyInternalError();
|
|
2750
2748
|
}
|
|
2749
|
+
}
|
|
2751
2750
|
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2751
|
+
/* build children list */
|
|
2752
|
+
PyObject* children_list = PyList_New(kept_children.size());
|
|
2753
|
+
if (!children_list) {
|
|
2754
|
+
X_Py_DECREF_ALL(kept_children);
|
|
2755
|
+
Py_DECREF(attributes);
|
|
2756
|
+
throw PyInternalError();
|
|
2757
|
+
}
|
|
2755
2758
|
|
|
2756
|
-
|
|
2757
|
-
|
|
2759
|
+
for (size_t i = 0; i < kept_children.size(); ++i)
|
|
2760
|
+
PyList_SET_ITEM(children_list, i, kept_children[i]); // steals refs
|
|
2758
2761
|
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
+
PyObject* children_attr =
|
|
2763
|
+
build_ast_node("HogQLXAttribute", "{s:s#,s:O}", "name", "children", (Py_ssize_t)8, "value", children_list);
|
|
2764
|
+
if (!children_attr) {
|
|
2765
|
+
Py_DECREF(children_list);
|
|
2766
|
+
Py_DECREF(attributes);
|
|
2767
|
+
throw PyInternalError();
|
|
2768
|
+
}
|
|
2762
2769
|
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2770
|
+
if (PyList_Append(attributes, children_attr) == -1) {
|
|
2771
|
+
Py_DECREF(children_attr);
|
|
2772
|
+
Py_DECREF(attributes);
|
|
2773
|
+
throw PyInternalError();
|
|
2774
|
+
}
|
|
2775
|
+
Py_DECREF(children_attr); // list now owns it
|
|
2767
2776
|
}
|
|
2768
2777
|
|
|
2769
|
-
PyObject
|
|
2770
|
-
"HogQLXTag",
|
|
2771
|
-
"{s:s#,s:N}",
|
|
2772
|
-
"kind", opening.data(), (Py_ssize_t)opening.size(),
|
|
2773
|
-
"attributes", attributes
|
|
2778
|
+
PyObject* ret = build_ast_node(
|
|
2779
|
+
"HogQLXTag", "{s:s#,s:N}", "kind", opening.data(), (Py_ssize_t)opening.size(), "attributes", attributes
|
|
2774
2780
|
);
|
|
2775
2781
|
if (!ret) {
|
|
2776
|
-
|
|
2777
|
-
|
|
2782
|
+
Py_DECREF(attributes);
|
|
2783
|
+
throw PyInternalError();
|
|
2778
2784
|
}
|
|
2779
2785
|
return ret;
|
|
2780
2786
|
}
|
|
2781
2787
|
|
|
2782
|
-
VISIT(Placeholder) {
|
|
2783
|
-
RETURN_NEW_AST_NODE("Placeholder", "{s:N}", "expr", visitAsPyObject(ctx->columnExpr()));
|
|
2784
|
-
}
|
|
2788
|
+
VISIT(Placeholder) { RETURN_NEW_AST_NODE("Placeholder", "{s:N}", "expr", visitAsPyObject(ctx->columnExpr())); }
|
|
2785
2789
|
|
|
2786
2790
|
VISIT_UNSUPPORTED(EnumValue)
|
|
2787
2791
|
|
|
@@ -2828,8 +2832,8 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
2828
2832
|
// If it's a Field, get `chain` and see if there's exactly one item in it.
|
|
2829
2833
|
PyObject* chain = PyObject_GetAttrString(expr, "chain");
|
|
2830
2834
|
if (!chain) {
|
|
2831
|
-
|
|
2832
|
-
|
|
2835
|
+
Py_DECREF(expr);
|
|
2836
|
+
throw PyInternalError();
|
|
2833
2837
|
}
|
|
2834
2838
|
|
|
2835
2839
|
if (PyList_Check(chain) && PyList_Size(chain) == 1) {
|
|
@@ -2867,8 +2871,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
2867
2871
|
}
|
|
2868
2872
|
|
|
2869
2873
|
// Build a Call node: ast.Call(name=<func_name>, args=[<select_node>])
|
|
2870
|
-
PyObject* call_node =
|
|
2871
|
-
build_ast_node("Call", "{s:s#,s:N}", "name", func_name, size, "args", args_list);
|
|
2874
|
+
PyObject* call_node = build_ast_node("Call", "{s:s#,s:N}", "name", func_name, size, "args", args_list);
|
|
2872
2875
|
Py_DECREF(chain);
|
|
2873
2876
|
Py_DECREF(expr);
|
|
2874
2877
|
|
|
@@ -2904,8 +2907,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
|
|
|
2904
2907
|
PyList_SET_ITEM(args_list, 0, select_node);
|
|
2905
2908
|
|
|
2906
2909
|
// Return ast.ExprCall(expr=expr, args=args_list).
|
|
2907
|
-
PyObject* expr_call_node =
|
|
2908
|
-
build_ast_node("ExprCall", "{s:N,s:N}", "expr", expr, "args", args_list);
|
|
2910
|
+
PyObject* expr_call_node = build_ast_node("ExprCall", "{s:N,s:N}", "expr", expr, "args", args_list);
|
|
2909
2911
|
if (!expr_call_node) {
|
|
2910
2912
|
Py_DECREF(expr);
|
|
2911
2913
|
Py_DECREF(select_node);
|
|
@@ -3030,52 +3032,63 @@ parser_state* get_module_state(PyObject* module) {
|
|
|
3030
3032
|
return static_cast<parser_state*>(PyModule_GetState(module));
|
|
3031
3033
|
}
|
|
3032
3034
|
|
|
3035
|
+
// Include JSON parser methods (depends on get_module_state and HogQLErrorListener)
|
|
3036
|
+
#include "parser_json_python.cpp"
|
|
3037
|
+
|
|
3033
3038
|
// MODULE METHODS
|
|
3034
3039
|
|
|
3035
|
-
#define METHOD_PARSE_NODE(PASCAL_CASE, CAMEL_CASE, SNAKE_CASE)
|
|
3036
|
-
static PyObject* method_parse_##SNAKE_CASE(PyObject* self, PyObject* args, PyObject* kwargs) {
|
|
3037
|
-
parser_state* state = get_module_state(self);
|
|
3038
|
-
const char* str;
|
|
3039
|
-
int internal = 0;
|
|
3040
|
-
static const char* kwlist[] = {"input", "is_internal", NULL};
|
|
3041
|
-
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|p", (char**)kwlist, &str, &internal)) {
|
|
3042
|
-
return NULL;
|
|
3043
|
-
}
|
|
3044
|
-
auto input_stream = new antlr4::ANTLRInputStream(str, strnlen(str, 65536));
|
|
3045
|
-
auto lexer = new HogQLLexer(input_stream);
|
|
3046
|
-
auto stream = new antlr4::CommonTokenStream(lexer);
|
|
3047
|
-
auto parser = new HogQLParser(stream);
|
|
3048
|
-
parser->removeErrorListeners();
|
|
3049
|
-
auto error_listener = new HogQLErrorListener(str);
|
|
3050
|
-
parser->addErrorListener(error_listener);
|
|
3051
|
-
HogQLParser::PASCAL_CASE##Context* parse_tree;
|
|
3052
|
-
try {
|
|
3053
|
-
parse_tree = parser->CAMEL_CASE();
|
|
3054
|
-
} catch HANDLE_HOGQL_ERROR(
|
|
3055
|
-
|
|
3056
|
-
catch (const antlr4::EmptyStackException
|
|
3057
|
-
delete error_listener;
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3040
|
+
#define METHOD_PARSE_NODE(PASCAL_CASE, CAMEL_CASE, SNAKE_CASE) \
|
|
3041
|
+
static PyObject* method_parse_##SNAKE_CASE(PyObject* self, PyObject* args, PyObject* kwargs) { \
|
|
3042
|
+
parser_state* state = get_module_state(self); \
|
|
3043
|
+
const char* str; \
|
|
3044
|
+
int internal = 0; \
|
|
3045
|
+
static const char* kwlist[] = {"input", "is_internal", NULL}; \
|
|
3046
|
+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|p", (char**)kwlist, &str, &internal)) { \
|
|
3047
|
+
return NULL; \
|
|
3048
|
+
} \
|
|
3049
|
+
auto input_stream = new antlr4::ANTLRInputStream(str, strnlen(str, 65536)); \
|
|
3050
|
+
auto lexer = new HogQLLexer(input_stream); \
|
|
3051
|
+
auto stream = new antlr4::CommonTokenStream(lexer); \
|
|
3052
|
+
auto parser = new HogQLParser(stream); \
|
|
3053
|
+
parser->removeErrorListeners(); \
|
|
3054
|
+
auto error_listener = new HogQLErrorListener(str); \
|
|
3055
|
+
parser->addErrorListener(error_listener); \
|
|
3056
|
+
HogQLParser::PASCAL_CASE##Context* parse_tree; \
|
|
3057
|
+
try { \
|
|
3058
|
+
parse_tree = parser->CAMEL_CASE(); \
|
|
3059
|
+
} catch HANDLE_HOGQL_ERROR( \
|
|
3060
|
+
SyntaxError, delete error_listener; delete parser; delete stream; delete lexer; delete input_stream; \
|
|
3061
|
+
) catch (const antlr4::EmptyStackException& e) { \
|
|
3062
|
+
delete error_listener; \
|
|
3063
|
+
delete parser; \
|
|
3064
|
+
delete stream; \
|
|
3065
|
+
delete lexer; \
|
|
3066
|
+
delete input_stream; \
|
|
3067
|
+
PyObject* error_type = PyObject_GetAttrString(state->errors_module, "SyntaxError"); \
|
|
3068
|
+
if (error_type) { \
|
|
3069
|
+
PyErr_SetString(error_type, "Unmatched curly bracket"); \
|
|
3070
|
+
} \
|
|
3071
|
+
return NULL; \
|
|
3072
|
+
} catch (...) { \
|
|
3073
|
+
delete error_listener; \
|
|
3074
|
+
delete parser; \
|
|
3075
|
+
delete stream; \
|
|
3076
|
+
delete lexer; \
|
|
3077
|
+
delete input_stream; \
|
|
3078
|
+
PyObject* error_type = PyObject_GetAttrString(state->errors_module, "ParsingError"); \
|
|
3079
|
+
if (error_type) { \
|
|
3080
|
+
PyErr_SetString(error_type, "Unexpected Antlr exception in C++ parser"); \
|
|
3081
|
+
} \
|
|
3082
|
+
return NULL; \
|
|
3083
|
+
}; \
|
|
3084
|
+
HogQLParseTreeConverter converter = HogQLParseTreeConverter(state, internal == 1); \
|
|
3085
|
+
PyObject* result_node = converter.visitAsPyObjectFinal(parse_tree); \
|
|
3086
|
+
delete error_listener; \
|
|
3087
|
+
delete parser; \
|
|
3088
|
+
delete stream; \
|
|
3089
|
+
delete lexer; \
|
|
3090
|
+
delete input_stream; \
|
|
3091
|
+
return result_node; \
|
|
3079
3092
|
}
|
|
3080
3093
|
|
|
3081
3094
|
METHOD_PARSE_NODE(Expr, expr, expr)
|
|
@@ -3126,6 +3139,29 @@ static PyMethodDef parser_methods[] = {
|
|
|
3126
3139
|
.ml_meth = method_parse_string_literal_text,
|
|
3127
3140
|
.ml_flags = METH_VARARGS,
|
|
3128
3141
|
.ml_doc = "Unquote the string (an identifier or a string literal))"},
|
|
3142
|
+
|
|
3143
|
+
// JSON output methods
|
|
3144
|
+
{.ml_name = "parse_expr_json",
|
|
3145
|
+
.ml_meth = (PyCFunction)method_parse_expr_json,
|
|
3146
|
+
.ml_flags = METH_VARARGS | METH_KEYWORDS,
|
|
3147
|
+
.ml_doc = "Parse the HogQL expression string into a JSON AST"},
|
|
3148
|
+
{.ml_name = "parse_order_expr_json",
|
|
3149
|
+
.ml_meth = (PyCFunction)method_parse_order_expr_json,
|
|
3150
|
+
.ml_flags = METH_VARARGS | METH_KEYWORDS,
|
|
3151
|
+
.ml_doc = "Parse the ORDER BY clause string into a JSON AST"},
|
|
3152
|
+
{.ml_name = "parse_select_json",
|
|
3153
|
+
.ml_meth = (PyCFunction)method_parse_select_json,
|
|
3154
|
+
.ml_flags = METH_VARARGS | METH_KEYWORDS,
|
|
3155
|
+
.ml_doc = "Parse the HogQL SELECT statement string into a JSON AST"},
|
|
3156
|
+
{.ml_name = "parse_full_template_string_json",
|
|
3157
|
+
.ml_meth = (PyCFunction)method_parse_full_template_string_json,
|
|
3158
|
+
.ml_flags = METH_VARARGS | METH_KEYWORDS,
|
|
3159
|
+
.ml_doc = "Parse a Hog template string into a JSON AST"},
|
|
3160
|
+
{.ml_name = "parse_program_json",
|
|
3161
|
+
.ml_meth = (PyCFunction)method_parse_program_json,
|
|
3162
|
+
.ml_flags = METH_VARARGS | METH_KEYWORDS,
|
|
3163
|
+
.ml_doc = "Parse a Hog program into a JSON AST"},
|
|
3164
|
+
|
|
3129
3165
|
{NULL, NULL, 0, NULL}
|
|
3130
3166
|
};
|
|
3131
3167
|
|
|
@@ -3181,4 +3217,4 @@ static struct PyModuleDef parser = {
|
|
|
3181
3217
|
|
|
3182
3218
|
PyMODINIT_FUNC PyInit_hogql_parser(void) {
|
|
3183
3219
|
return PyModuleDef_Init(&parser);
|
|
3184
|
-
}
|
|
3220
|
+
}
|
|
@@ -18,14 +18,17 @@ module = Extension(
|
|
|
18
18
|
"HogQLParserVisitor.cpp",
|
|
19
19
|
"error.cpp",
|
|
20
20
|
"string.cpp",
|
|
21
|
-
"
|
|
21
|
+
"json.cpp",
|
|
22
|
+
"parser_python.cpp",
|
|
22
23
|
],
|
|
23
|
-
include_dirs=
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
include_dirs=(
|
|
25
|
+
[
|
|
26
|
+
f"{homebrew_location}/include/",
|
|
27
|
+
f"{homebrew_location}/include/antlr4-runtime/",
|
|
28
|
+
]
|
|
29
|
+
if is_macos
|
|
30
|
+
else ["/usr/include/", "/usr/include/antlr4-runtime/"]
|
|
31
|
+
),
|
|
29
32
|
library_dirs=[f"{homebrew_location}/lib/"] if is_macos else ["/usr/lib/", "/usr/lib64/"],
|
|
30
33
|
libraries=["antlr4-runtime"],
|
|
31
34
|
extra_compile_args=["-std=c++20"],
|
|
@@ -33,7 +36,7 @@ module = Extension(
|
|
|
33
36
|
|
|
34
37
|
setup(
|
|
35
38
|
name="hogql_parser",
|
|
36
|
-
version="1.2
|
|
39
|
+
version="1.3.2",
|
|
37
40
|
url="https://github.com/PostHog/posthog/tree/master/common/hogql_parser",
|
|
38
41
|
description="HogQL parser for internal PostHog use",
|
|
39
42
|
author="PostHog Inc.",
|
|
@@ -46,9 +49,9 @@ setup(
|
|
|
46
49
|
include_package_data=True,
|
|
47
50
|
ext_modules=[module],
|
|
48
51
|
python_requires=">=3.10",
|
|
52
|
+
license="MIT",
|
|
49
53
|
classifiers=[
|
|
50
54
|
"Development Status :: 5 - Production/Stable",
|
|
51
|
-
"License :: OSI Approved :: MIT License",
|
|
52
55
|
"Operating System :: MacOS",
|
|
53
56
|
"Operating System :: POSIX :: Linux",
|
|
54
57
|
"Programming Language :: Python",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|