Skip to content

Commit

Permalink
feat(hogqlx): nested tags (#18213)
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusandra authored Oct 27, 2023
1 parent f393cc7 commit 4ea13f1
Show file tree
Hide file tree
Showing 15 changed files with 1,276 additions and 892 deletions.
986 changes: 555 additions & 431 deletions hogql_parser/HogQLParser.cpp

Large diffs are not rendered by default.

31 changes: 30 additions & 1 deletion hogql_parser/HogQLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1457,17 +1457,46 @@ class HogQLParser : public antlr4::Parser {
class HogqlxTagElementContext : public antlr4::ParserRuleContext {
public:
HogqlxTagElementContext(antlr4::ParserRuleContext *parent, size_t invokingState);

HogqlxTagElementContext() = default;
void copyFrom(HogqlxTagElementContext *context);
using antlr4::ParserRuleContext::copyFrom;

virtual size_t getRuleIndex() const override;


};

class HogqlxTagElementClosedContext : public HogqlxTagElementContext {
public:
HogqlxTagElementClosedContext(HogqlxTagElementContext *ctx);

antlr4::tree::TerminalNode *LT();
IdentifierContext *identifier();
antlr4::tree::TerminalNode *SLASH();
antlr4::tree::TerminalNode *GT();
std::vector<HogqlxTagAttributeContext *> hogqlxTagAttribute();
HogqlxTagAttributeContext* hogqlxTagAttribute(size_t i);

virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
};

class HogqlxTagElementNestedContext : public HogqlxTagElementContext {
public:
HogqlxTagElementNestedContext(HogqlxTagElementContext *ctx);

std::vector<antlr4::tree::TerminalNode *> LT();
antlr4::tree::TerminalNode* LT(size_t i);
std::vector<IdentifierContext *> identifier();
IdentifierContext* identifier(size_t i);
std::vector<antlr4::tree::TerminalNode *> GT();
antlr4::tree::TerminalNode* GT(size_t i);
antlr4::tree::TerminalNode *SLASH();
std::vector<HogqlxTagAttributeContext *> hogqlxTagAttribute();
HogqlxTagAttributeContext* hogqlxTagAttribute(size_t i);
HogqlxTagElementContext *hogqlxTagElement();

virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;

};

HogqlxTagElementContext* hogqlxTagElement();
Expand Down
2 changes: 1 addition & 1 deletion hogql_parser/HogQLParser.interp

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion hogql_parser/HogQLParserBaseVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,11 @@ class HogQLParserBaseVisitor : public HogQLParserVisitor {
return visitChildren(ctx);
}

virtual std::any visitHogqlxTagElement(HogQLParser::HogqlxTagElementContext *ctx) override {
virtual std::any visitHogqlxTagElementClosed(HogQLParser::HogqlxTagElementClosedContext *ctx) override {
return visitChildren(ctx);
}

virtual std::any visitHogqlxTagElementNested(HogQLParser::HogqlxTagElementNestedContext *ctx) override {
return visitChildren(ctx);
}

Expand Down
4 changes: 3 additions & 1 deletion hogql_parser/HogQLParserVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,9 @@ class HogQLParserVisitor : public antlr4::tree::AbstractParseTreeVisitor {

virtual std::any visitColumnLambdaExpr(HogQLParser::ColumnLambdaExprContext *context) = 0;

virtual std::any visitHogqlxTagElement(HogQLParser::HogqlxTagElementContext *context) = 0;
virtual std::any visitHogqlxTagElementClosed(HogQLParser::HogqlxTagElementClosedContext *context) = 0;

virtual std::any visitHogqlxTagElementNested(HogQLParser::HogqlxTagElementNestedContext *context) = 0;

virtual std::any visitHogqlxTagAttribute(HogQLParser::HogqlxTagAttributeContext *context) = 0;

Expand Down
42 changes: 41 additions & 1 deletion hogql_parser/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1244,7 +1244,7 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
);
}

VISIT(HogqlxTagElement) {
VISIT(HogqlxTagElementClosed) {
string kind = visitAsString(ctx->identifier());
PyObject* tag_element = build_ast_node(
"HogQLXTag", "{s:s#,s:N}",
Expand All @@ -1254,6 +1254,46 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor {
return tag_element;
}

VISIT(HogqlxTagElementNested) {
string opening = visitAsString(ctx->identifier(0));
string closing = visitAsString(ctx->identifier(1));
if (opening != closing) {
throw HogQLSyntaxException("Opening and closing HogQLX tags must match. Got " + opening + " and " + closing);
}

auto tag_element_ctx = ctx->hogqlxTagElement();
auto tag_attribute_ctx = ctx->hogqlxTagAttribute();
PyObject* attributes = PyList_New(tag_attribute_ctx.size() + (tag_element_ctx ? 1 : 0));
bool found_source = false;
for (size_t i = 0; i < tag_attribute_ctx.size(); i++) {
PyObject* object = visitAsPyObject(tag_attribute_ctx[i]);
PyList_SET_ITEM(attributes, i, object);

PyObject* name = PyObject_GetAttrString(object, "name");
if (PyObject_RichCompareBool(name, PyUnicode_FromString("source"), Py_EQ)) {
found_source = true;
}
Py_DECREF(name);
}

if (tag_element_ctx) {
if (found_source) {
Py_DECREF(attributes);
throw HogQLSyntaxException("Nested HogQLX tags cannot have a source attribute");
}
PyList_SET_ITEM(attributes, tag_attribute_ctx.size(), build_ast_node(
"HogQLXAttribute", "{s:s#,s:N}", "name", "source", 6, "value", visitAsPyObject(ctx->hogqlxTagElement())
));
}

PyObject* tag_element = build_ast_node(
"HogQLXTag", "{s:s#,s:N}",
"kind", opening.data(), opening.size(),
"attributes", attributes
);
return tag_element;
}

VISIT(Placeholder) {
string name = visitAsString(ctx->identifier());
return build_ast_node("Placeholder", "{s:s#}", "field", name.data(), name.size());
Expand Down
2 changes: 1 addition & 1 deletion hogql_parser/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

setup(
name="hogql_parser",
version="0.1.11",
version="0.1.12",
url="https://github.com/PostHog/posthog/tree/master/hogql_parser",
author="PostHog Inc.",
author_email="[email protected]",
Expand Down
7 changes: 5 additions & 2 deletions posthog/hogql/grammar/HogQLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,10 @@ columnLambdaExpr:
;


hogqlxTagElement: LT identifier hogqlxTagAttribute* SLASH GT;
hogqlxTagElement
: LT identifier hogqlxTagAttribute* SLASH GT # HogqlxTagElementClosed
| LT identifier hogqlxTagAttribute* GT hogqlxTagElement? LT SLASH identifier GT # HogqlxTagElementNested
;
hogqlxTagAttribute
: identifier '=' STRING_LITERAL
| identifier '=' LBRACE columnExpr RBRACE
Expand All @@ -196,7 +199,7 @@ tableExpr
| tableFunctionExpr # TableExprFunction
| LPAREN selectUnionStmt RPAREN # TableExprSubquery
| tableExpr (alias | AS identifier) # TableExprAlias
| hogqlxTagElement # TableExprTag
| hogqlxTagElement # TableExprTag
| placeholder # TableExprPlaceholder
;
tableFunctionExpr: identifier LPAREN tableArgList? RPAREN;
Expand Down
2 changes: 1 addition & 1 deletion posthog/hogql/grammar/HogQLParser.interp

Large diffs are not rendered by default.

Loading

0 comments on commit 4ea13f1

Please sign in to comment.