hogql-parser 1.0.0__tar.gz → 1.0.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hogql_parser
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: HogQL parser for internal PostHog use
5
5
  Home-page: https://github.com/PostHog/posthog/tree/master/hogql_parser
6
6
  Author: PostHog Inc.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hogql-parser
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: HogQL parser for internal PostHog use
5
5
  Home-page: https://github.com/PostHog/posthog/tree/master/hogql_parser
6
6
  Author: PostHog Inc.
@@ -17,7 +17,7 @@
17
17
  throw NotImplementedException("Unsupported rule: " #RULE); \
18
18
  }
19
19
 
20
- #define HANDLE_HOGQL_EXCEPTION(TYPE) \
20
+ #define HANDLE_HOGQL_EXCEPTION(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; \
@@ -37,11 +37,12 @@
37
37
  err_indicator = PyObject_SetAttrString(py_err, "end", py_end); \
38
38
  if (err_indicator == -1) goto exit##TYPE; \
39
39
  PyErr_SetObject(error_type, py_err); \
40
- exit##TYPE:; \
40
+ exit##TYPE :; \
41
41
  Py_XDECREF(py_end); \
42
42
  Py_XDECREF(py_start); \
43
43
  Py_XDECREF(py_err); \
44
44
  Py_XDECREF(error_type); \
45
+ OTHER_CLEANUP \
45
46
  return NULL; \
46
47
  }
47
48
 
@@ -98,6 +99,7 @@ PyObject* X_PyList_FromStrings(const vector<string>& items) {
98
99
  class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
99
100
  private:
100
101
  parser_state* state;
102
+ bool is_internal;
101
103
 
102
104
  const vector<string> RESERVED_KEYWORDS = {"true", "false", "null", "team_id"};
103
105
 
@@ -151,7 +153,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
151
153
  #undef IS_AST_NODE_INSTANCE_IMPL
152
154
 
153
155
  public:
154
- HogQLParseTreeConverter(parser_state* state) : state(state) {}
156
+ HogQLParseTreeConverter(parser_state* state, bool is_internal) : state(state), is_internal(is_internal) {}
155
157
 
156
158
  any visit(antlr4::tree::ParseTree* tree) override {
157
159
  // Find the start and stop indices of the parse tree node
@@ -169,17 +171,18 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
169
171
  start = ctx->getStart()->getStartIndex();
170
172
  stop = ctx->getStop()->getStopIndex();
171
173
  }
172
- // Visit the parse tree node
174
+ // Visit the parse tree node (while making sure that nodes/errors have spans - except for internal expressions)
173
175
  any node;
174
176
  try {
175
177
  node = tree->accept(this);
176
178
  } catch (const SyntaxException& e) {
177
- if (e.start == 0 && e.end == 0) { // If start and end are unset, rethrow with the current start and stop
179
+ // If start and end are unset, rethrow with the current start and stop
180
+ if (!is_internal && e.start == 0 && e.end == 0) {
178
181
  throw SyntaxException(e.what(), start, stop + 1);
179
182
  }
180
183
  throw;
181
184
  }
182
- if (node.has_value() && node.type() == typeid(PyObject*)) {
185
+ if (!is_internal && node.has_value() && node.type() == typeid(PyObject*)) {
183
186
  PyObject* py_node = any_cast<PyObject*>(node);
184
187
  if (py_node) {
185
188
  int is_ast = is_ast_node_instance(py_node);
@@ -218,8 +221,9 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
218
221
  PyObject* visitAsPyObjectFinal(antlr4::tree::ParseTree* tree) {
219
222
  try {
220
223
  return visitAsPyObject(tree);
221
- } catch HANDLE_HOGQL_EXCEPTION(SyntaxException) catch HANDLE_HOGQL_EXCEPTION(NotImplementedException
222
- ) catch HANDLE_HOGQL_EXCEPTION(ParsingException) catch (const PyInternalException& e) {
224
+ } catch HANDLE_HOGQL_EXCEPTION(SyntaxException, ) catch HANDLE_HOGQL_EXCEPTION(
225
+ NotImplementedException,
226
+ ) catch HANDLE_HOGQL_EXCEPTION(ParsingException, ) catch (const PyInternalException& e) {
223
227
  return NULL;
224
228
  } catch (const bad_any_cast& e) {
225
229
  PyObject* error_type = PyObject_GetAttrString(state->errors_module, "ParsingException");
@@ -1789,7 +1793,8 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
1789
1793
  VISIT(TableFunctionExpr) {
1790
1794
  string table_name = visitAsString(ctx->identifier());
1791
1795
  auto table_args_ctx = ctx->tableArgList();
1792
- PyObject* table_args = table_args_ctx ? visitAsPyObject(table_args_ctx) : Py_NewRef(Py_None);
1796
+ PyObject* table_args = table_args_ctx ? visitAsPyObject(table_args_ctx) : PyList_New(0);
1797
+ if (!table_args) throw PyInternalException();
1793
1798
  PyObject* table = build_ast_node("Field", "{s:[s#]}", "chain", table_name.data(), table_name.size());
1794
1799
  if (!table) {
1795
1800
  Py_DECREF(table_args);
@@ -2030,32 +2035,35 @@ parser_state* get_module_state(PyObject* module) {
2030
2035
 
2031
2036
  // MODULE METHODS
2032
2037
 
2033
- #define METHOD_PARSE_NODE(PASCAL_CASE, CAMEL_CASE, SNAKE_CASE) \
2034
- static PyObject* method_parse_##SNAKE_CASE(PyObject* self, PyObject* args) { \
2035
- parser_state* state = get_module_state(self); \
2036
- const char* str; \
2037
- if (!PyArg_ParseTuple(args, "s", &str)) { \
2038
- return NULL; \
2039
- } \
2040
- auto input_stream = new antlr4::ANTLRInputStream(str, strnlen(str, 65536)); \
2041
- auto lexer = new HogQLLexer(input_stream); \
2042
- auto stream = new antlr4::CommonTokenStream(lexer); \
2043
- auto parser = new HogQLParser(stream); \
2044
- parser->removeErrorListeners(); \
2045
- auto error_listener = new HogQLErrorListener(str); \
2046
- parser->addErrorListener(error_listener); \
2047
- HogQLParser::PASCAL_CASE##Context* parse_tree; \
2048
- try { \
2049
- parse_tree = parser->CAMEL_CASE(); \
2050
- } catch HANDLE_HOGQL_EXCEPTION(SyntaxException); \
2051
- HogQLParseTreeConverter converter = HogQLParseTreeConverter(state); \
2052
- PyObject* result_node = converter.visitAsPyObjectFinal(parse_tree); \
2053
- delete error_listener; \
2054
- delete parser; \
2055
- delete stream; \
2056
- delete lexer; \
2057
- delete input_stream; \
2058
- return result_node; \
2038
+ #define METHOD_PARSE_NODE(PASCAL_CASE, CAMEL_CASE, SNAKE_CASE) \
2039
+ static PyObject* method_parse_##SNAKE_CASE(PyObject* self, PyObject* args, PyObject* kwargs) { \
2040
+ parser_state* state = get_module_state(self); \
2041
+ const char* str; \
2042
+ int internal = 0; \
2043
+ static const char* kwlist[] = {"input", "is_internal", NULL}; \
2044
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|p", (char**)kwlist, &str, &internal)) { \
2045
+ return NULL; \
2046
+ } \
2047
+ auto input_stream = new antlr4::ANTLRInputStream(str, strnlen(str, 65536)); \
2048
+ auto lexer = new HogQLLexer(input_stream); \
2049
+ auto stream = new antlr4::CommonTokenStream(lexer); \
2050
+ auto parser = new HogQLParser(stream); \
2051
+ parser->removeErrorListeners(); \
2052
+ auto error_listener = new HogQLErrorListener(str); \
2053
+ parser->addErrorListener(error_listener); \
2054
+ HogQLParser::PASCAL_CASE##Context* parse_tree; \
2055
+ try { \
2056
+ parse_tree = parser->CAMEL_CASE(); \
2057
+ } catch HANDLE_HOGQL_EXCEPTION(SyntaxException, delete error_listener; delete parser; delete stream; delete lexer; \
2058
+ delete input_stream;); \
2059
+ HogQLParseTreeConverter converter = HogQLParseTreeConverter(state, internal == 1); \
2060
+ PyObject* result_node = converter.visitAsPyObjectFinal(parse_tree); \
2061
+ delete error_listener; \
2062
+ delete parser; \
2063
+ delete stream; \
2064
+ delete lexer; \
2065
+ delete input_stream; \
2066
+ return result_node; \
2059
2067
  }
2060
2068
 
2061
2069
  METHOD_PARSE_NODE(Expr, expr, expr)
@@ -2073,7 +2081,7 @@ static PyObject* method_unquote_string(PyObject* self, PyObject* args) {
2073
2081
  string unquoted_string;
2074
2082
  try {
2075
2083
  unquoted_string = unquote_string(str);
2076
- } catch HANDLE_HOGQL_EXCEPTION(SyntaxException);
2084
+ } catch HANDLE_HOGQL_EXCEPTION(SyntaxException, );
2077
2085
  return PyUnicode_FromStringAndSize(unquoted_string.data(), unquoted_string.size());
2078
2086
  }
2079
2087
 
@@ -2081,22 +2089,23 @@ static PyObject* method_unquote_string(PyObject* self, PyObject* args) {
2081
2089
 
2082
2090
  static PyMethodDef parser_methods[] = {
2083
2091
  {.ml_name = "parse_expr",
2084
- .ml_meth = method_parse_expr,
2085
- .ml_flags = METH_VARARGS,
2092
+ .ml_meth = (PyCFunction)method_parse_expr,
2093
+ .ml_flags = METH_VARARGS | METH_KEYWORDS,
2086
2094
  .ml_doc = "Parse the HogQL expression string into an AST"},
2087
2095
  {.ml_name = "parse_order_expr",
2088
- .ml_meth = method_parse_order_expr,
2089
- .ml_flags = METH_VARARGS,
2096
+ .ml_meth = (PyCFunction)method_parse_order_expr,
2097
+ .ml_flags = METH_VARARGS | METH_KEYWORDS,
2090
2098
  .ml_doc = "Parse the ORDER BY clause string into an AST"},
2091
2099
  {.ml_name = "parse_select",
2092
- .ml_meth = method_parse_select,
2093
- .ml_flags = METH_VARARGS,
2100
+ .ml_meth = (PyCFunction)method_parse_select,
2101
+ .ml_flags = METH_VARARGS | METH_KEYWORDS,
2094
2102
  .ml_doc = "Parse the HogQL SELECT statement string into an AST"},
2095
2103
  {.ml_name = "unquote_string",
2096
2104
  .ml_meth = method_unquote_string,
2097
2105
  .ml_flags = METH_VARARGS,
2098
2106
  .ml_doc = "Unquote the string (an identifier or a string literal))"},
2099
- {NULL, NULL, 0, NULL}};
2107
+ {NULL, NULL, 0, NULL}
2108
+ };
2100
2109
 
2101
2110
  static int parser_modexec(PyObject* module) {
2102
2111
  parser_state* state = get_module_state(module);
@@ -2118,7 +2127,8 @@ static int parser_modexec(PyObject* module) {
2118
2127
  static PyModuleDef_Slot parser_slots[] = {
2119
2128
  {Py_mod_exec, (void*)parser_modexec}, // If Python were written in C++, then Py_mod_exec would be typed better, but
2120
2129
  // because it's in C, it expects a void pointer
2121
- {0, NULL}};
2130
+ {0, NULL}
2131
+ };
2122
2132
 
2123
2133
  static int parser_traverse(PyObject* module, visitproc visit, void* arg) {
2124
2134
  parser_state* state = get_module_state(module);
@@ -32,7 +32,7 @@ module = Extension(
32
32
 
33
33
  setup(
34
34
  name="hogql_parser",
35
- version="1.0.0",
35
+ version="1.0.2",
36
36
  url="https://github.com/PostHog/posthog/tree/master/hogql_parser",
37
37
  author="PostHog Inc.",
38
38
  author_email="hey@posthog.com",
@@ -40,7 +40,7 @@ string unquote_string(string text) {
40
40
  boost::replace_all(text, "\\r", "\r");
41
41
  boost::replace_all(text, "\\t", "\t");
42
42
  boost::replace_all(text, "\\v", "\v");
43
- boost::replace_all(text, "\\0", ""); // NUL characters are ignored
43
+ boost::replace_all(text, "\\0", ""); // NUL characters are ignored
44
44
  boost::replace_all(text, "\\\\", "\\");
45
45
 
46
46
  return text;
File without changes
File without changes
File without changes