From f71d5bf8f16604c7aa292f94d83ea1634cf9b64c Mon Sep 17 00:00:00 2001 From: Tushar Vats Date: Thu, 2 Apr 2026 09:22:11 +0530 Subject: [PATCH] fix: added validations for having expression (#10286) * fix: added validations for having expression * fix: added extra validation and unit tests * fix: added antlr based parsing for validation * fix: added more unit tests * fix: removed validation on having in range request validations * fix: generated lexer files and added more unit tests * fix: edge cases * fix: added cmnd to scripts for generating lexer * fix: use std libg sorting instead of selection sort * fix: support implicit and * fix: allow bare not in expression * fix: added suggestion for having expression * fix: typo * fix: added more unit tests, handle white space difference in aggregation exp and having exp * fix: added support for in and not, updated errors * fix: added support for brackets list * fix: lint error * fix: handle non spaced expression --------- Co-authored-by: Srikanth Chekuri --- grammar/HavingExpression.g4 | 256 ++ pkg/contextlinks/alert_link_visitor.go | 2 +- .../grammar/FilterQuery.interp | 0 .../grammar/FilterQuery.tokens | 0 .../grammar/FilterQueryLexer.interp | 0 .../grammar/FilterQueryLexer.tokens | 0 .../grammar/filterquery_base_listener.go | 0 .../grammar/filterquery_base_visitor.go | 0 .../grammar/filterquery_lexer.go | 0 .../grammar/filterquery_listener.go | 0 .../grammar/filterquery_parser.go | 0 .../grammar/filterquery_visitor.go | 0 .../grammar/HavingExpression.interp | 81 + .../grammar/HavingExpression.tokens | 42 + .../grammar/HavingExpressionLexer.interp | 97 + .../grammar/HavingExpressionLexer.tokens | 42 + .../grammar/havingexpression_base_listener.go | 130 + .../grammar/havingexpression_base_visitor.go | 81 + .../grammar/havingexpression_lexer.go | 232 + .../grammar/havingexpression_listener.go | 118 + .../grammar/havingexpression_parser.go | 3834 +++++++++++++++++ .../grammar/havingexpression_visitor.go | 64 + pkg/querybuilder/filter_error_listener.go | 42 + .../having_expression_validator.go | 496 +++ pkg/querybuilder/having_rewriter.go | 80 +- pkg/querybuilder/having_rewriter_test.go | 1050 +++++ pkg/querybuilder/never_true.go | 2 +- pkg/querybuilder/query_to_keys.go | 2 +- pkg/querybuilder/where_clause_visitor.go | 2 +- pkg/querybuilder/where_clause_visitor_test.go | 2 +- pkg/telemetrylogs/filter_expr_logs_test.go | 8 +- pkg/telemetrylogs/statement_builder.go | 15 +- pkg/telemetrylogs/stmt_builder_having_test.go | 33 +- pkg/telemetrymetrics/statement_builder.go | 15 +- pkg/telemetrytraces/statement_builder.go | 15 +- pkg/telemetrytraces/trace_id_extractor.go | 2 +- .../trace_operator_cte_builder.go | 23 +- .../querybuildertypesv5/validation.go | 12 + pkg/variables/variable_replace_visitor.go | 2 +- scripts/grammar/generate-go-parser.sh | 3 +- 40 files changed, 6696 insertions(+), 87 deletions(-) create mode 100644 grammar/HavingExpression.g4 rename pkg/parser/{ => filterquery}/grammar/FilterQuery.interp (100%) rename pkg/parser/{ => filterquery}/grammar/FilterQuery.tokens (100%) rename pkg/parser/{ => filterquery}/grammar/FilterQueryLexer.interp (100%) rename pkg/parser/{ => filterquery}/grammar/FilterQueryLexer.tokens (100%) rename pkg/parser/{ => filterquery}/grammar/filterquery_base_listener.go (100%) rename pkg/parser/{ => filterquery}/grammar/filterquery_base_visitor.go (100%) rename pkg/parser/{ => filterquery}/grammar/filterquery_lexer.go (100%) rename pkg/parser/{ => filterquery}/grammar/filterquery_listener.go (100%) rename pkg/parser/{ => filterquery}/grammar/filterquery_parser.go (100%) rename pkg/parser/{ => filterquery}/grammar/filterquery_visitor.go (100%) create mode 100644 pkg/parser/havingexpression/grammar/HavingExpression.interp create mode 100644 pkg/parser/havingexpression/grammar/HavingExpression.tokens create mode 100644 pkg/parser/havingexpression/grammar/HavingExpressionLexer.interp create mode 100644 pkg/parser/havingexpression/grammar/HavingExpressionLexer.tokens create mode 100644 pkg/parser/havingexpression/grammar/havingexpression_base_listener.go create mode 100644 pkg/parser/havingexpression/grammar/havingexpression_base_visitor.go create mode 100644 pkg/parser/havingexpression/grammar/havingexpression_lexer.go create mode 100644 pkg/parser/havingexpression/grammar/havingexpression_listener.go create mode 100644 pkg/parser/havingexpression/grammar/havingexpression_parser.go create mode 100644 pkg/parser/havingexpression/grammar/havingexpression_visitor.go create mode 100644 pkg/querybuilder/having_expression_validator.go create mode 100644 pkg/querybuilder/having_rewriter_test.go diff --git a/grammar/HavingExpression.g4 b/grammar/HavingExpression.g4 new file mode 100644 index 000000000..c737c7d7e --- /dev/null +++ b/grammar/HavingExpression.g4 @@ -0,0 +1,256 @@ +grammar HavingExpression; + +/* + * Parser Rules + */ + +query + : expression EOF + ; + +// Expression with standard boolean precedence: +// - parentheses > NOT > AND > OR +expression + : orExpression + ; + +// OR expressions +orExpression + : andExpression ( OR andExpression )* + ; + +// AND expressions + optional chaining with implicit AND if no OR is present +andExpression + : primary ( AND primary | primary )* + ; + +// Primary: an optionally negated expression. +// NOT can be applied to a parenthesized expression or a bare comparison / IN-test. +// E.g.: NOT (count() > 100 AND sum(bytes) < 500) +// NOT count() > 100 +// count() IN (1, 2, 3) -- NOT here is part of comparison, see below +// count() NOT IN (1, 2, 3) +primary + : NOT? LPAREN orExpression RPAREN + | NOT? comparison + ; + +/* + * Comparison between two arithmetic operands, or an IN / NOT IN membership test. + * E.g.: count() > 100, total_duration >= 500, __result_0 != 0 + * count() IN (1, 2, 3), sum(bytes) NOT IN (0, -1) + * count() IN [1, 2, 3], sum(bytes) NOT IN [0, -1] + */ +comparison + : operand compOp operand + | operand NOT? IN LPAREN inList RPAREN + | operand NOT? IN LBRACK inList RBRACK + ; + +compOp + : EQUALS + | NOT_EQUALS + | NEQ + | LT + | LE + | GT + | GE + ; + +/* + * IN-list: a comma-separated list of numeric literals, each optionally signed. + * E.g.: (1, 2, 3), [100, 200, 500], (-1, 0, 1) + */ +inList + : signedNumber ( COMMA signedNumber )* + ; + +/* + * A signed number allows an optional leading +/- before a numeric literal. + * Used in IN-lists where a bare minus is unambiguous (no binary operand to the left). + */ +signedNumber + : (PLUS | MINUS)? NUMBER + ; + +/* + * Operands support additive arithmetic (+/-). + * E.g.: sum(a) + sum(b) > 1000, count() - 10 > 0 + */ +operand + : operand (PLUS | MINUS) term + | term + ; + +/* + * Terms support multiplicative arithmetic (*, /, %) + * E.g.: count() * 2 > 100, sum(bytes) / 1024 > 10 + */ +term + : term (STAR | SLASH | PERCENT) factor + | factor + ; + +/* + * Factors: atoms, parenthesized operands, or unary-signed sub-factors. + * E.g.: (sum(a) + sum(b)) * 2 > 100, -count() > 0, -(avg(x) + 1) > 0 + * -10 (unary minus applied to the literal 10), count() - 10 > 0 + * + * Note: the NUMBER rule does NOT include a leading sign, so `-10` is always + * tokenised as MINUS followed by NUMBER(10). Unary minus in `factor` handles + * negative literals just as it handles negative function calls or identifiers, + * and the binary MINUS in `operand` handles `count()-10` naturally. + */ +factor + : (PLUS | MINUS) factor + | LPAREN operand RPAREN + | atom + ; + +/* + * Atoms are the basic building blocks of arithmetic operands: + * - aggregate function calls: count(), sum(bytes), avg(duration) + * - identifier references: aliases, result refs (__result, __result_0, __result0) + * - numeric literals: 100, 0.5, 1e6 + * - string literals: 'xyz' — recognized so we can give a friendly error + * + * String literals in HAVING are always invalid (aggregator results are numeric), + * but we accept them here so the visitor can produce a clear error message instead + * of a raw syntax error. + */ +atom + : functionCall + | identifier + | NUMBER + | STRING + ; + +/* + * Aggregate function calls, e.g.: + * count(), sum(bytes), avg(duration_nano) + * countIf(level='error'), sumIf(bytes, status > 400) + * p99(duration), avg(sum(cpu_usage)) + * + * Function arguments are parsed as a permissive token sequence (funcArgToken+) + * so that complex aggregation expressions — including nested function calls and + * filter predicates with string literals — can be referenced verbatim in the + * HAVING expression. The visitor looks up the full call text (whitespace-free, + * via ctx.GetText()) in the column map, which stores normalized (space-stripped) + * aggregation expression keys. + */ +functionCall + : IDENTIFIER LPAREN functionArgList? RPAREN + ; + +functionArgList + : funcArg ( COMMA funcArg )* + ; + +/* + * A single function argument is one or more consecutive arg-tokens. + * Commas at the top level separate arguments; closing parens terminate the list. + */ +funcArg + : funcArgToken+ + ; + +/* + * Permissive token set for function argument content. Covers: + * - simple identifiers: bytes, duration + * - string literals: 'error', "info" + * - numeric literals: 200, 3.14 + * - comparison operators: level='error', status > 400 + * - arithmetic operators: x + y + * - boolean connectives: level='error' AND status=200 + * - balanced parens: nested calls like sum(duration) + */ +funcArgToken + : IDENTIFIER + | STRING + | NUMBER + | BOOL + | EQUALS | NOT_EQUALS | NEQ | LT | LE | GT | GE + | PLUS | MINUS | STAR | SLASH | PERCENT + | NOT | AND | OR + | LPAREN funcArgToken* RPAREN + ; + +// Identifier references: aliases, field names, result references +// Examples: total_logs, error_count, __result, __result_0, __result0, p99 +identifier + : IDENTIFIER + ; + +/* + * Lexer Rules + */ + +// Punctuation +LPAREN : '(' ; +RPAREN : ')' ; +LBRACK : '[' ; +RBRACK : ']' ; +COMMA : ',' ; + +// Comparison operators +EQUALS : '=' | '==' ; +NOT_EQUALS : '!=' ; +NEQ : '<>' ; // alternate not-equals operator +LT : '<' ; +LE : '<=' ; +GT : '>' ; +GE : '>=' ; + +// Arithmetic operators +PLUS : '+' ; +MINUS : '-' ; +STAR : '*' ; +SLASH : '/' ; +PERCENT : '%' ; + +// Boolean logic (case-insensitive) +NOT : [Nn][Oo][Tt] ; +AND : [Aa][Nn][Dd] ; +OR : [Oo][Rr] ; +IN : [Ii][Nn] ; + +// Boolean constants (case-insensitive) +BOOL + : [Tt][Rr][Uu][Ee] + | [Ff][Aa][Ll][Ss][Ee] + ; + +fragment SIGN : [+-] ; + +// Numbers: digits, optional decimal, optional scientific notation. +// No leading sign — a leading +/- is always a separate PLUS/MINUS token, which +// lets the parser treat it as either a binary operator (count()-10) or unary +// sign (-count(), -10). Signed exponents like 1e-3 remain valid. +// E.g.: 100, 0.5, 1.5e3, .75 +NUMBER + : DIGIT+ ('.' DIGIT*)? ([eE] SIGN? DIGIT+)? + | '.' DIGIT+ ([eE] SIGN? DIGIT+)? + ; + +// Identifiers: start with a letter or underscore, followed by alphanumeric/underscores. +// Optionally dotted for nested field paths. +// Covers: count, sum, p99, total_logs, error_count, __result, __result_0, __result0, +// service.name, span.duration +IDENTIFIER + : [a-zA-Z_] [a-zA-Z0-9_]* ( '.' [a-zA-Z_] [a-zA-Z0-9_]* )* + ; + +// Quoted string literals (single or double-quoted). +// These are valid tokens inside function arguments (e.g. countIf(level='error')) +// but are always rejected in comparison-operand position by the visitor. +STRING + : '\'' (~'\'')* '\'' + | '"' (~'"')* '"' + ; + +// Skip whitespace +WS + : [ \t\r\n]+ -> skip + ; + +fragment DIGIT : [0-9] ; diff --git a/pkg/contextlinks/alert_link_visitor.go b/pkg/contextlinks/alert_link_visitor.go index 6462db063..612883088 100644 --- a/pkg/contextlinks/alert_link_visitor.go +++ b/pkg/contextlinks/alert_link_visitor.go @@ -5,7 +5,7 @@ import ( "slices" "strings" - parser "github.com/SigNoz/signoz/pkg/parser/grammar" + parser "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar" "github.com/antlr4-go/antlr/v4" "golang.org/x/exp/maps" diff --git a/pkg/parser/grammar/FilterQuery.interp b/pkg/parser/filterquery/grammar/FilterQuery.interp similarity index 100% rename from pkg/parser/grammar/FilterQuery.interp rename to pkg/parser/filterquery/grammar/FilterQuery.interp diff --git a/pkg/parser/grammar/FilterQuery.tokens b/pkg/parser/filterquery/grammar/FilterQuery.tokens similarity index 100% rename from pkg/parser/grammar/FilterQuery.tokens rename to pkg/parser/filterquery/grammar/FilterQuery.tokens diff --git a/pkg/parser/grammar/FilterQueryLexer.interp b/pkg/parser/filterquery/grammar/FilterQueryLexer.interp similarity index 100% rename from pkg/parser/grammar/FilterQueryLexer.interp rename to pkg/parser/filterquery/grammar/FilterQueryLexer.interp diff --git a/pkg/parser/grammar/FilterQueryLexer.tokens b/pkg/parser/filterquery/grammar/FilterQueryLexer.tokens similarity index 100% rename from pkg/parser/grammar/FilterQueryLexer.tokens rename to pkg/parser/filterquery/grammar/FilterQueryLexer.tokens diff --git a/pkg/parser/grammar/filterquery_base_listener.go b/pkg/parser/filterquery/grammar/filterquery_base_listener.go similarity index 100% rename from pkg/parser/grammar/filterquery_base_listener.go rename to pkg/parser/filterquery/grammar/filterquery_base_listener.go diff --git a/pkg/parser/grammar/filterquery_base_visitor.go b/pkg/parser/filterquery/grammar/filterquery_base_visitor.go similarity index 100% rename from pkg/parser/grammar/filterquery_base_visitor.go rename to pkg/parser/filterquery/grammar/filterquery_base_visitor.go diff --git a/pkg/parser/grammar/filterquery_lexer.go b/pkg/parser/filterquery/grammar/filterquery_lexer.go similarity index 100% rename from pkg/parser/grammar/filterquery_lexer.go rename to pkg/parser/filterquery/grammar/filterquery_lexer.go diff --git a/pkg/parser/grammar/filterquery_listener.go b/pkg/parser/filterquery/grammar/filterquery_listener.go similarity index 100% rename from pkg/parser/grammar/filterquery_listener.go rename to pkg/parser/filterquery/grammar/filterquery_listener.go diff --git a/pkg/parser/grammar/filterquery_parser.go b/pkg/parser/filterquery/grammar/filterquery_parser.go similarity index 100% rename from pkg/parser/grammar/filterquery_parser.go rename to pkg/parser/filterquery/grammar/filterquery_parser.go diff --git a/pkg/parser/grammar/filterquery_visitor.go b/pkg/parser/filterquery/grammar/filterquery_visitor.go similarity index 100% rename from pkg/parser/grammar/filterquery_visitor.go rename to pkg/parser/filterquery/grammar/filterquery_visitor.go diff --git a/pkg/parser/havingexpression/grammar/HavingExpression.interp b/pkg/parser/havingexpression/grammar/HavingExpression.interp new file mode 100644 index 000000000..041a1a456 --- /dev/null +++ b/pkg/parser/havingexpression/grammar/HavingExpression.interp @@ -0,0 +1,81 @@ +token literal names: +null +'(' +')' +'[' +']' +',' +null +'!=' +'<>' +'<' +'<=' +'>' +'>=' +'+' +'-' +'*' +'/' +'%' +null +null +null +null +null +null +null +null +null + +token symbolic names: +null +LPAREN +RPAREN +LBRACK +RBRACK +COMMA +EQUALS +NOT_EQUALS +NEQ +LT +LE +GT +GE +PLUS +MINUS +STAR +SLASH +PERCENT +NOT +AND +OR +IN +BOOL +NUMBER +IDENTIFIER +STRING +WS + +rule names: +query +expression +orExpression +andExpression +primary +comparison +compOp +inList +signedNumber +operand +term +factor +atom +functionCall +functionArgList +funcArg +funcArgToken +identifier + + +atn: +[4, 1, 26, 199, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 5, 2, 45, 8, 2, 10, 2, 12, 2, 48, 9, 2, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 54, 8, 3, 10, 3, 12, 3, 57, 9, 3, 1, 4, 3, 4, 60, 8, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 67, 8, 4, 1, 4, 3, 4, 70, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 78, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 87, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 94, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 101, 8, 7, 10, 7, 12, 7, 104, 9, 7, 1, 8, 3, 8, 107, 8, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 117, 8, 9, 10, 9, 12, 9, 120, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 128, 8, 10, 10, 10, 12, 10, 131, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 140, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 146, 8, 12, 1, 13, 1, 13, 1, 13, 3, 13, 151, 8, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 5, 14, 158, 8, 14, 10, 14, 12, 14, 161, 9, 14, 1, 15, 4, 15, 164, 8, 15, 11, 15, 12, 15, 165, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 5, 16, 189, 8, 16, 10, 16, 12, 16, 192, 9, 16, 1, 16, 3, 16, 195, 8, 16, 1, 17, 1, 17, 1, 17, 0, 2, 18, 20, 18, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 0, 3, 1, 0, 6, 12, 1, 0, 13, 14, 1, 0, 15, 17, 222, 0, 36, 1, 0, 0, 0, 2, 39, 1, 0, 0, 0, 4, 41, 1, 0, 0, 0, 6, 49, 1, 0, 0, 0, 8, 69, 1, 0, 0, 0, 10, 93, 1, 0, 0, 0, 12, 95, 1, 0, 0, 0, 14, 97, 1, 0, 0, 0, 16, 106, 1, 0, 0, 0, 18, 110, 1, 0, 0, 0, 20, 121, 1, 0, 0, 0, 22, 139, 1, 0, 0, 0, 24, 145, 1, 0, 0, 0, 26, 147, 1, 0, 0, 0, 28, 154, 1, 0, 0, 0, 30, 163, 1, 0, 0, 0, 32, 194, 1, 0, 0, 0, 34, 196, 1, 0, 0, 0, 36, 37, 3, 2, 1, 0, 37, 38, 5, 0, 0, 1, 38, 1, 1, 0, 0, 0, 39, 40, 3, 4, 2, 0, 40, 3, 1, 0, 0, 0, 41, 46, 3, 6, 3, 0, 42, 43, 5, 20, 0, 0, 43, 45, 3, 6, 3, 0, 44, 42, 1, 0, 0, 0, 45, 48, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 5, 1, 0, 0, 0, 48, 46, 1, 0, 0, 0, 49, 55, 3, 8, 4, 0, 50, 51, 5, 19, 0, 0, 51, 54, 3, 8, 4, 0, 52, 54, 3, 8, 4, 0, 53, 50, 1, 0, 0, 0, 53, 52, 1, 0, 0, 0, 54, 57, 1, 0, 0, 0, 55, 53, 1, 0, 0, 0, 55, 56, 1, 0, 0, 0, 56, 7, 1, 0, 0, 0, 57, 55, 1, 0, 0, 0, 58, 60, 5, 18, 0, 0, 59, 58, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 61, 1, 0, 0, 0, 61, 62, 5, 1, 0, 0, 62, 63, 3, 4, 2, 0, 63, 64, 5, 2, 0, 0, 64, 70, 1, 0, 0, 0, 65, 67, 5, 18, 0, 0, 66, 65, 1, 0, 0, 0, 66, 67, 1, 0, 0, 0, 67, 68, 1, 0, 0, 0, 68, 70, 3, 10, 5, 0, 69, 59, 1, 0, 0, 0, 69, 66, 1, 0, 0, 0, 70, 9, 1, 0, 0, 0, 71, 72, 3, 18, 9, 0, 72, 73, 3, 12, 6, 0, 73, 74, 3, 18, 9, 0, 74, 94, 1, 0, 0, 0, 75, 77, 3, 18, 9, 0, 76, 78, 5, 18, 0, 0, 77, 76, 1, 0, 0, 0, 77, 78, 1, 0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 80, 5, 21, 0, 0, 80, 81, 5, 1, 0, 0, 81, 82, 3, 14, 7, 0, 82, 83, 5, 2, 0, 0, 83, 94, 1, 0, 0, 0, 84, 86, 3, 18, 9, 0, 85, 87, 5, 18, 0, 0, 86, 85, 1, 0, 0, 0, 86, 87, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 89, 5, 21, 0, 0, 89, 90, 5, 3, 0, 0, 90, 91, 3, 14, 7, 0, 91, 92, 5, 4, 0, 0, 92, 94, 1, 0, 0, 0, 93, 71, 1, 0, 0, 0, 93, 75, 1, 0, 0, 0, 93, 84, 1, 0, 0, 0, 94, 11, 1, 0, 0, 0, 95, 96, 7, 0, 0, 0, 96, 13, 1, 0, 0, 0, 97, 102, 3, 16, 8, 0, 98, 99, 5, 5, 0, 0, 99, 101, 3, 16, 8, 0, 100, 98, 1, 0, 0, 0, 101, 104, 1, 0, 0, 0, 102, 100, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 15, 1, 0, 0, 0, 104, 102, 1, 0, 0, 0, 105, 107, 7, 1, 0, 0, 106, 105, 1, 0, 0, 0, 106, 107, 1, 0, 0, 0, 107, 108, 1, 0, 0, 0, 108, 109, 5, 23, 0, 0, 109, 17, 1, 0, 0, 0, 110, 111, 6, 9, -1, 0, 111, 112, 3, 20, 10, 0, 112, 118, 1, 0, 0, 0, 113, 114, 10, 2, 0, 0, 114, 115, 7, 1, 0, 0, 115, 117, 3, 20, 10, 0, 116, 113, 1, 0, 0, 0, 117, 120, 1, 0, 0, 0, 118, 116, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 19, 1, 0, 0, 0, 120, 118, 1, 0, 0, 0, 121, 122, 6, 10, -1, 0, 122, 123, 3, 22, 11, 0, 123, 129, 1, 0, 0, 0, 124, 125, 10, 2, 0, 0, 125, 126, 7, 2, 0, 0, 126, 128, 3, 22, 11, 0, 127, 124, 1, 0, 0, 0, 128, 131, 1, 0, 0, 0, 129, 127, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 21, 1, 0, 0, 0, 131, 129, 1, 0, 0, 0, 132, 133, 7, 1, 0, 0, 133, 140, 3, 22, 11, 0, 134, 135, 5, 1, 0, 0, 135, 136, 3, 18, 9, 0, 136, 137, 5, 2, 0, 0, 137, 140, 1, 0, 0, 0, 138, 140, 3, 24, 12, 0, 139, 132, 1, 0, 0, 0, 139, 134, 1, 0, 0, 0, 139, 138, 1, 0, 0, 0, 140, 23, 1, 0, 0, 0, 141, 146, 3, 26, 13, 0, 142, 146, 3, 34, 17, 0, 143, 146, 5, 23, 0, 0, 144, 146, 5, 25, 0, 0, 145, 141, 1, 0, 0, 0, 145, 142, 1, 0, 0, 0, 145, 143, 1, 0, 0, 0, 145, 144, 1, 0, 0, 0, 146, 25, 1, 0, 0, 0, 147, 148, 5, 24, 0, 0, 148, 150, 5, 1, 0, 0, 149, 151, 3, 28, 14, 0, 150, 149, 1, 0, 0, 0, 150, 151, 1, 0, 0, 0, 151, 152, 1, 0, 0, 0, 152, 153, 5, 2, 0, 0, 153, 27, 1, 0, 0, 0, 154, 159, 3, 30, 15, 0, 155, 156, 5, 5, 0, 0, 156, 158, 3, 30, 15, 0, 157, 155, 1, 0, 0, 0, 158, 161, 1, 0, 0, 0, 159, 157, 1, 0, 0, 0, 159, 160, 1, 0, 0, 0, 160, 29, 1, 0, 0, 0, 161, 159, 1, 0, 0, 0, 162, 164, 3, 32, 16, 0, 163, 162, 1, 0, 0, 0, 164, 165, 1, 0, 0, 0, 165, 163, 1, 0, 0, 0, 165, 166, 1, 0, 0, 0, 166, 31, 1, 0, 0, 0, 167, 195, 5, 24, 0, 0, 168, 195, 5, 25, 0, 0, 169, 195, 5, 23, 0, 0, 170, 195, 5, 22, 0, 0, 171, 195, 5, 6, 0, 0, 172, 195, 5, 7, 0, 0, 173, 195, 5, 8, 0, 0, 174, 195, 5, 9, 0, 0, 175, 195, 5, 10, 0, 0, 176, 195, 5, 11, 0, 0, 177, 195, 5, 12, 0, 0, 178, 195, 5, 13, 0, 0, 179, 195, 5, 14, 0, 0, 180, 195, 5, 15, 0, 0, 181, 195, 5, 16, 0, 0, 182, 195, 5, 17, 0, 0, 183, 195, 5, 18, 0, 0, 184, 195, 5, 19, 0, 0, 185, 195, 5, 20, 0, 0, 186, 190, 5, 1, 0, 0, 187, 189, 3, 32, 16, 0, 188, 187, 1, 0, 0, 0, 189, 192, 1, 0, 0, 0, 190, 188, 1, 0, 0, 0, 190, 191, 1, 0, 0, 0, 191, 193, 1, 0, 0, 0, 192, 190, 1, 0, 0, 0, 193, 195, 5, 2, 0, 0, 194, 167, 1, 0, 0, 0, 194, 168, 1, 0, 0, 0, 194, 169, 1, 0, 0, 0, 194, 170, 1, 0, 0, 0, 194, 171, 1, 0, 0, 0, 194, 172, 1, 0, 0, 0, 194, 173, 1, 0, 0, 0, 194, 174, 1, 0, 0, 0, 194, 175, 1, 0, 0, 0, 194, 176, 1, 0, 0, 0, 194, 177, 1, 0, 0, 0, 194, 178, 1, 0, 0, 0, 194, 179, 1, 0, 0, 0, 194, 180, 1, 0, 0, 0, 194, 181, 1, 0, 0, 0, 194, 182, 1, 0, 0, 0, 194, 183, 1, 0, 0, 0, 194, 184, 1, 0, 0, 0, 194, 185, 1, 0, 0, 0, 194, 186, 1, 0, 0, 0, 195, 33, 1, 0, 0, 0, 196, 197, 5, 24, 0, 0, 197, 35, 1, 0, 0, 0, 20, 46, 53, 55, 59, 66, 69, 77, 86, 93, 102, 106, 118, 129, 139, 145, 150, 159, 165, 190, 194] \ No newline at end of file diff --git a/pkg/parser/havingexpression/grammar/HavingExpression.tokens b/pkg/parser/havingexpression/grammar/HavingExpression.tokens new file mode 100644 index 000000000..840f9a76f --- /dev/null +++ b/pkg/parser/havingexpression/grammar/HavingExpression.tokens @@ -0,0 +1,42 @@ +LPAREN=1 +RPAREN=2 +LBRACK=3 +RBRACK=4 +COMMA=5 +EQUALS=6 +NOT_EQUALS=7 +NEQ=8 +LT=9 +LE=10 +GT=11 +GE=12 +PLUS=13 +MINUS=14 +STAR=15 +SLASH=16 +PERCENT=17 +NOT=18 +AND=19 +OR=20 +IN=21 +BOOL=22 +NUMBER=23 +IDENTIFIER=24 +STRING=25 +WS=26 +'('=1 +')'=2 +'['=3 +']'=4 +','=5 +'!='=7 +'<>'=8 +'<'=9 +'<='=10 +'>'=11 +'>='=12 +'+'=13 +'-'=14 +'*'=15 +'/'=16 +'%'=17 diff --git a/pkg/parser/havingexpression/grammar/HavingExpressionLexer.interp b/pkg/parser/havingexpression/grammar/HavingExpressionLexer.interp new file mode 100644 index 000000000..806e3ae28 --- /dev/null +++ b/pkg/parser/havingexpression/grammar/HavingExpressionLexer.interp @@ -0,0 +1,97 @@ +token literal names: +null +'(' +')' +'[' +']' +',' +null +'!=' +'<>' +'<' +'<=' +'>' +'>=' +'+' +'-' +'*' +'/' +'%' +null +null +null +null +null +null +null +null +null + +token symbolic names: +null +LPAREN +RPAREN +LBRACK +RBRACK +COMMA +EQUALS +NOT_EQUALS +NEQ +LT +LE +GT +GE +PLUS +MINUS +STAR +SLASH +PERCENT +NOT +AND +OR +IN +BOOL +NUMBER +IDENTIFIER +STRING +WS + +rule names: +LPAREN +RPAREN +LBRACK +RBRACK +COMMA +EQUALS +NOT_EQUALS +NEQ +LT +LE +GT +GE +PLUS +MINUS +STAR +SLASH +PERCENT +NOT +AND +OR +IN +BOOL +SIGN +NUMBER +IDENTIFIER +STRING +WS +DIGIT + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[4, 0, 26, 216, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 71, 8, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 122, 8, 21, 1, 22, 1, 22, 1, 23, 4, 23, 127, 8, 23, 11, 23, 12, 23, 128, 1, 23, 1, 23, 5, 23, 133, 8, 23, 10, 23, 12, 23, 136, 9, 23, 3, 23, 138, 8, 23, 1, 23, 1, 23, 3, 23, 142, 8, 23, 1, 23, 4, 23, 145, 8, 23, 11, 23, 12, 23, 146, 3, 23, 149, 8, 23, 1, 23, 1, 23, 4, 23, 153, 8, 23, 11, 23, 12, 23, 154, 1, 23, 1, 23, 3, 23, 159, 8, 23, 1, 23, 4, 23, 162, 8, 23, 11, 23, 12, 23, 163, 3, 23, 166, 8, 23, 3, 23, 168, 8, 23, 1, 24, 1, 24, 5, 24, 172, 8, 24, 10, 24, 12, 24, 175, 9, 24, 1, 24, 1, 24, 1, 24, 5, 24, 180, 8, 24, 10, 24, 12, 24, 183, 9, 24, 5, 24, 185, 8, 24, 10, 24, 12, 24, 188, 9, 24, 1, 25, 1, 25, 5, 25, 192, 8, 25, 10, 25, 12, 25, 195, 9, 25, 1, 25, 1, 25, 1, 25, 5, 25, 200, 8, 25, 10, 25, 12, 25, 203, 9, 25, 1, 25, 3, 25, 206, 8, 25, 1, 26, 4, 26, 209, 8, 26, 11, 26, 12, 26, 210, 1, 26, 1, 26, 1, 27, 1, 27, 0, 0, 28, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 0, 47, 23, 49, 24, 51, 25, 53, 26, 55, 0, 1, 0, 19, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0, 84, 84, 116, 116, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 82, 82, 114, 114, 2, 0, 73, 73, 105, 105, 2, 0, 85, 85, 117, 117, 2, 0, 69, 69, 101, 101, 2, 0, 70, 70, 102, 102, 2, 0, 76, 76, 108, 108, 2, 0, 83, 83, 115, 115, 2, 0, 43, 43, 45, 45, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 1, 0, 39, 39, 1, 0, 34, 34, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 233, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 1, 57, 1, 0, 0, 0, 3, 59, 1, 0, 0, 0, 5, 61, 1, 0, 0, 0, 7, 63, 1, 0, 0, 0, 9, 65, 1, 0, 0, 0, 11, 70, 1, 0, 0, 0, 13, 72, 1, 0, 0, 0, 15, 75, 1, 0, 0, 0, 17, 78, 1, 0, 0, 0, 19, 80, 1, 0, 0, 0, 21, 83, 1, 0, 0, 0, 23, 85, 1, 0, 0, 0, 25, 88, 1, 0, 0, 0, 27, 90, 1, 0, 0, 0, 29, 92, 1, 0, 0, 0, 31, 94, 1, 0, 0, 0, 33, 96, 1, 0, 0, 0, 35, 98, 1, 0, 0, 0, 37, 102, 1, 0, 0, 0, 39, 106, 1, 0, 0, 0, 41, 109, 1, 0, 0, 0, 43, 121, 1, 0, 0, 0, 45, 123, 1, 0, 0, 0, 47, 167, 1, 0, 0, 0, 49, 169, 1, 0, 0, 0, 51, 205, 1, 0, 0, 0, 53, 208, 1, 0, 0, 0, 55, 214, 1, 0, 0, 0, 57, 58, 5, 40, 0, 0, 58, 2, 1, 0, 0, 0, 59, 60, 5, 41, 0, 0, 60, 4, 1, 0, 0, 0, 61, 62, 5, 91, 0, 0, 62, 6, 1, 0, 0, 0, 63, 64, 5, 93, 0, 0, 64, 8, 1, 0, 0, 0, 65, 66, 5, 44, 0, 0, 66, 10, 1, 0, 0, 0, 67, 71, 5, 61, 0, 0, 68, 69, 5, 61, 0, 0, 69, 71, 5, 61, 0, 0, 70, 67, 1, 0, 0, 0, 70, 68, 1, 0, 0, 0, 71, 12, 1, 0, 0, 0, 72, 73, 5, 33, 0, 0, 73, 74, 5, 61, 0, 0, 74, 14, 1, 0, 0, 0, 75, 76, 5, 60, 0, 0, 76, 77, 5, 62, 0, 0, 77, 16, 1, 0, 0, 0, 78, 79, 5, 60, 0, 0, 79, 18, 1, 0, 0, 0, 80, 81, 5, 60, 0, 0, 81, 82, 5, 61, 0, 0, 82, 20, 1, 0, 0, 0, 83, 84, 5, 62, 0, 0, 84, 22, 1, 0, 0, 0, 85, 86, 5, 62, 0, 0, 86, 87, 5, 61, 0, 0, 87, 24, 1, 0, 0, 0, 88, 89, 5, 43, 0, 0, 89, 26, 1, 0, 0, 0, 90, 91, 5, 45, 0, 0, 91, 28, 1, 0, 0, 0, 92, 93, 5, 42, 0, 0, 93, 30, 1, 0, 0, 0, 94, 95, 5, 47, 0, 0, 95, 32, 1, 0, 0, 0, 96, 97, 5, 37, 0, 0, 97, 34, 1, 0, 0, 0, 98, 99, 7, 0, 0, 0, 99, 100, 7, 1, 0, 0, 100, 101, 7, 2, 0, 0, 101, 36, 1, 0, 0, 0, 102, 103, 7, 3, 0, 0, 103, 104, 7, 0, 0, 0, 104, 105, 7, 4, 0, 0, 105, 38, 1, 0, 0, 0, 106, 107, 7, 1, 0, 0, 107, 108, 7, 5, 0, 0, 108, 40, 1, 0, 0, 0, 109, 110, 7, 6, 0, 0, 110, 111, 7, 0, 0, 0, 111, 42, 1, 0, 0, 0, 112, 113, 7, 2, 0, 0, 113, 114, 7, 5, 0, 0, 114, 115, 7, 7, 0, 0, 115, 122, 7, 8, 0, 0, 116, 117, 7, 9, 0, 0, 117, 118, 7, 3, 0, 0, 118, 119, 7, 10, 0, 0, 119, 120, 7, 11, 0, 0, 120, 122, 7, 8, 0, 0, 121, 112, 1, 0, 0, 0, 121, 116, 1, 0, 0, 0, 122, 44, 1, 0, 0, 0, 123, 124, 7, 12, 0, 0, 124, 46, 1, 0, 0, 0, 125, 127, 3, 55, 27, 0, 126, 125, 1, 0, 0, 0, 127, 128, 1, 0, 0, 0, 128, 126, 1, 0, 0, 0, 128, 129, 1, 0, 0, 0, 129, 137, 1, 0, 0, 0, 130, 134, 5, 46, 0, 0, 131, 133, 3, 55, 27, 0, 132, 131, 1, 0, 0, 0, 133, 136, 1, 0, 0, 0, 134, 132, 1, 0, 0, 0, 134, 135, 1, 0, 0, 0, 135, 138, 1, 0, 0, 0, 136, 134, 1, 0, 0, 0, 137, 130, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, 138, 148, 1, 0, 0, 0, 139, 141, 7, 8, 0, 0, 140, 142, 3, 45, 22, 0, 141, 140, 1, 0, 0, 0, 141, 142, 1, 0, 0, 0, 142, 144, 1, 0, 0, 0, 143, 145, 3, 55, 27, 0, 144, 143, 1, 0, 0, 0, 145, 146, 1, 0, 0, 0, 146, 144, 1, 0, 0, 0, 146, 147, 1, 0, 0, 0, 147, 149, 1, 0, 0, 0, 148, 139, 1, 0, 0, 0, 148, 149, 1, 0, 0, 0, 149, 168, 1, 0, 0, 0, 150, 152, 5, 46, 0, 0, 151, 153, 3, 55, 27, 0, 152, 151, 1, 0, 0, 0, 153, 154, 1, 0, 0, 0, 154, 152, 1, 0, 0, 0, 154, 155, 1, 0, 0, 0, 155, 165, 1, 0, 0, 0, 156, 158, 7, 8, 0, 0, 157, 159, 3, 45, 22, 0, 158, 157, 1, 0, 0, 0, 158, 159, 1, 0, 0, 0, 159, 161, 1, 0, 0, 0, 160, 162, 3, 55, 27, 0, 161, 160, 1, 0, 0, 0, 162, 163, 1, 0, 0, 0, 163, 161, 1, 0, 0, 0, 163, 164, 1, 0, 0, 0, 164, 166, 1, 0, 0, 0, 165, 156, 1, 0, 0, 0, 165, 166, 1, 0, 0, 0, 166, 168, 1, 0, 0, 0, 167, 126, 1, 0, 0, 0, 167, 150, 1, 0, 0, 0, 168, 48, 1, 0, 0, 0, 169, 173, 7, 13, 0, 0, 170, 172, 7, 14, 0, 0, 171, 170, 1, 0, 0, 0, 172, 175, 1, 0, 0, 0, 173, 171, 1, 0, 0, 0, 173, 174, 1, 0, 0, 0, 174, 186, 1, 0, 0, 0, 175, 173, 1, 0, 0, 0, 176, 177, 5, 46, 0, 0, 177, 181, 7, 13, 0, 0, 178, 180, 7, 14, 0, 0, 179, 178, 1, 0, 0, 0, 180, 183, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, 185, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 184, 176, 1, 0, 0, 0, 185, 188, 1, 0, 0, 0, 186, 184, 1, 0, 0, 0, 186, 187, 1, 0, 0, 0, 187, 50, 1, 0, 0, 0, 188, 186, 1, 0, 0, 0, 189, 193, 5, 39, 0, 0, 190, 192, 8, 15, 0, 0, 191, 190, 1, 0, 0, 0, 192, 195, 1, 0, 0, 0, 193, 191, 1, 0, 0, 0, 193, 194, 1, 0, 0, 0, 194, 196, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 196, 206, 5, 39, 0, 0, 197, 201, 5, 34, 0, 0, 198, 200, 8, 16, 0, 0, 199, 198, 1, 0, 0, 0, 200, 203, 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 204, 1, 0, 0, 0, 203, 201, 1, 0, 0, 0, 204, 206, 5, 34, 0, 0, 205, 189, 1, 0, 0, 0, 205, 197, 1, 0, 0, 0, 206, 52, 1, 0, 0, 0, 207, 209, 7, 17, 0, 0, 208, 207, 1, 0, 0, 0, 209, 210, 1, 0, 0, 0, 210, 208, 1, 0, 0, 0, 210, 211, 1, 0, 0, 0, 211, 212, 1, 0, 0, 0, 212, 213, 6, 26, 0, 0, 213, 54, 1, 0, 0, 0, 214, 215, 7, 18, 0, 0, 215, 56, 1, 0, 0, 0, 21, 0, 70, 121, 128, 134, 137, 141, 146, 148, 154, 158, 163, 165, 167, 173, 181, 186, 193, 201, 205, 210, 1, 6, 0, 0] \ No newline at end of file diff --git a/pkg/parser/havingexpression/grammar/HavingExpressionLexer.tokens b/pkg/parser/havingexpression/grammar/HavingExpressionLexer.tokens new file mode 100644 index 000000000..840f9a76f --- /dev/null +++ b/pkg/parser/havingexpression/grammar/HavingExpressionLexer.tokens @@ -0,0 +1,42 @@ +LPAREN=1 +RPAREN=2 +LBRACK=3 +RBRACK=4 +COMMA=5 +EQUALS=6 +NOT_EQUALS=7 +NEQ=8 +LT=9 +LE=10 +GT=11 +GE=12 +PLUS=13 +MINUS=14 +STAR=15 +SLASH=16 +PERCENT=17 +NOT=18 +AND=19 +OR=20 +IN=21 +BOOL=22 +NUMBER=23 +IDENTIFIER=24 +STRING=25 +WS=26 +'('=1 +')'=2 +'['=3 +']'=4 +','=5 +'!='=7 +'<>'=8 +'<'=9 +'<='=10 +'>'=11 +'>='=12 +'+'=13 +'-'=14 +'*'=15 +'/'=16 +'%'=17 diff --git a/pkg/parser/havingexpression/grammar/havingexpression_base_listener.go b/pkg/parser/havingexpression/grammar/havingexpression_base_listener.go new file mode 100644 index 000000000..ba9ce7c45 --- /dev/null +++ b/pkg/parser/havingexpression/grammar/havingexpression_base_listener.go @@ -0,0 +1,130 @@ +// Code generated from grammar/HavingExpression.g4 by ANTLR 4.13.2. DO NOT EDIT. + +package parser // HavingExpression + +import "github.com/antlr4-go/antlr/v4" + +// BaseHavingExpressionListener is a complete listener for a parse tree produced by HavingExpressionParser. +type BaseHavingExpressionListener struct{} + +var _ HavingExpressionListener = &BaseHavingExpressionListener{} + +// VisitTerminal is called when a terminal node is visited. +func (s *BaseHavingExpressionListener) VisitTerminal(node antlr.TerminalNode) {} + +// VisitErrorNode is called when an error node is visited. +func (s *BaseHavingExpressionListener) VisitErrorNode(node antlr.ErrorNode) {} + +// EnterEveryRule is called when any rule is entered. +func (s *BaseHavingExpressionListener) EnterEveryRule(ctx antlr.ParserRuleContext) {} + +// ExitEveryRule is called when any rule is exited. +func (s *BaseHavingExpressionListener) ExitEveryRule(ctx antlr.ParserRuleContext) {} + +// EnterQuery is called when production query is entered. +func (s *BaseHavingExpressionListener) EnterQuery(ctx *QueryContext) {} + +// ExitQuery is called when production query is exited. +func (s *BaseHavingExpressionListener) ExitQuery(ctx *QueryContext) {} + +// EnterExpression is called when production expression is entered. +func (s *BaseHavingExpressionListener) EnterExpression(ctx *ExpressionContext) {} + +// ExitExpression is called when production expression is exited. +func (s *BaseHavingExpressionListener) ExitExpression(ctx *ExpressionContext) {} + +// EnterOrExpression is called when production orExpression is entered. +func (s *BaseHavingExpressionListener) EnterOrExpression(ctx *OrExpressionContext) {} + +// ExitOrExpression is called when production orExpression is exited. +func (s *BaseHavingExpressionListener) ExitOrExpression(ctx *OrExpressionContext) {} + +// EnterAndExpression is called when production andExpression is entered. +func (s *BaseHavingExpressionListener) EnterAndExpression(ctx *AndExpressionContext) {} + +// ExitAndExpression is called when production andExpression is exited. +func (s *BaseHavingExpressionListener) ExitAndExpression(ctx *AndExpressionContext) {} + +// EnterPrimary is called when production primary is entered. +func (s *BaseHavingExpressionListener) EnterPrimary(ctx *PrimaryContext) {} + +// ExitPrimary is called when production primary is exited. +func (s *BaseHavingExpressionListener) ExitPrimary(ctx *PrimaryContext) {} + +// EnterComparison is called when production comparison is entered. +func (s *BaseHavingExpressionListener) EnterComparison(ctx *ComparisonContext) {} + +// ExitComparison is called when production comparison is exited. +func (s *BaseHavingExpressionListener) ExitComparison(ctx *ComparisonContext) {} + +// EnterCompOp is called when production compOp is entered. +func (s *BaseHavingExpressionListener) EnterCompOp(ctx *CompOpContext) {} + +// ExitCompOp is called when production compOp is exited. +func (s *BaseHavingExpressionListener) ExitCompOp(ctx *CompOpContext) {} + +// EnterInList is called when production inList is entered. +func (s *BaseHavingExpressionListener) EnterInList(ctx *InListContext) {} + +// ExitInList is called when production inList is exited. +func (s *BaseHavingExpressionListener) ExitInList(ctx *InListContext) {} + +// EnterSignedNumber is called when production signedNumber is entered. +func (s *BaseHavingExpressionListener) EnterSignedNumber(ctx *SignedNumberContext) {} + +// ExitSignedNumber is called when production signedNumber is exited. +func (s *BaseHavingExpressionListener) ExitSignedNumber(ctx *SignedNumberContext) {} + +// EnterOperand is called when production operand is entered. +func (s *BaseHavingExpressionListener) EnterOperand(ctx *OperandContext) {} + +// ExitOperand is called when production operand is exited. +func (s *BaseHavingExpressionListener) ExitOperand(ctx *OperandContext) {} + +// EnterTerm is called when production term is entered. +func (s *BaseHavingExpressionListener) EnterTerm(ctx *TermContext) {} + +// ExitTerm is called when production term is exited. +func (s *BaseHavingExpressionListener) ExitTerm(ctx *TermContext) {} + +// EnterFactor is called when production factor is entered. +func (s *BaseHavingExpressionListener) EnterFactor(ctx *FactorContext) {} + +// ExitFactor is called when production factor is exited. +func (s *BaseHavingExpressionListener) ExitFactor(ctx *FactorContext) {} + +// EnterAtom is called when production atom is entered. +func (s *BaseHavingExpressionListener) EnterAtom(ctx *AtomContext) {} + +// ExitAtom is called when production atom is exited. +func (s *BaseHavingExpressionListener) ExitAtom(ctx *AtomContext) {} + +// EnterFunctionCall is called when production functionCall is entered. +func (s *BaseHavingExpressionListener) EnterFunctionCall(ctx *FunctionCallContext) {} + +// ExitFunctionCall is called when production functionCall is exited. +func (s *BaseHavingExpressionListener) ExitFunctionCall(ctx *FunctionCallContext) {} + +// EnterFunctionArgList is called when production functionArgList is entered. +func (s *BaseHavingExpressionListener) EnterFunctionArgList(ctx *FunctionArgListContext) {} + +// ExitFunctionArgList is called when production functionArgList is exited. +func (s *BaseHavingExpressionListener) ExitFunctionArgList(ctx *FunctionArgListContext) {} + +// EnterFuncArg is called when production funcArg is entered. +func (s *BaseHavingExpressionListener) EnterFuncArg(ctx *FuncArgContext) {} + +// ExitFuncArg is called when production funcArg is exited. +func (s *BaseHavingExpressionListener) ExitFuncArg(ctx *FuncArgContext) {} + +// EnterFuncArgToken is called when production funcArgToken is entered. +func (s *BaseHavingExpressionListener) EnterFuncArgToken(ctx *FuncArgTokenContext) {} + +// ExitFuncArgToken is called when production funcArgToken is exited. +func (s *BaseHavingExpressionListener) ExitFuncArgToken(ctx *FuncArgTokenContext) {} + +// EnterIdentifier is called when production identifier is entered. +func (s *BaseHavingExpressionListener) EnterIdentifier(ctx *IdentifierContext) {} + +// ExitIdentifier is called when production identifier is exited. +func (s *BaseHavingExpressionListener) ExitIdentifier(ctx *IdentifierContext) {} diff --git a/pkg/parser/havingexpression/grammar/havingexpression_base_visitor.go b/pkg/parser/havingexpression/grammar/havingexpression_base_visitor.go new file mode 100644 index 000000000..3554bb040 --- /dev/null +++ b/pkg/parser/havingexpression/grammar/havingexpression_base_visitor.go @@ -0,0 +1,81 @@ +// Code generated from grammar/HavingExpression.g4 by ANTLR 4.13.2. DO NOT EDIT. + +package parser // HavingExpression + +import "github.com/antlr4-go/antlr/v4" + +type BaseHavingExpressionVisitor struct { + *antlr.BaseParseTreeVisitor +} + +func (v *BaseHavingExpressionVisitor) VisitQuery(ctx *QueryContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitExpression(ctx *ExpressionContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitOrExpression(ctx *OrExpressionContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitAndExpression(ctx *AndExpressionContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitPrimary(ctx *PrimaryContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitComparison(ctx *ComparisonContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitCompOp(ctx *CompOpContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitInList(ctx *InListContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitSignedNumber(ctx *SignedNumberContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitOperand(ctx *OperandContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitTerm(ctx *TermContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitFactor(ctx *FactorContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitAtom(ctx *AtomContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitFunctionCall(ctx *FunctionCallContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitFunctionArgList(ctx *FunctionArgListContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitFuncArg(ctx *FuncArgContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitFuncArgToken(ctx *FuncArgTokenContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseHavingExpressionVisitor) VisitIdentifier(ctx *IdentifierContext) interface{} { + return v.VisitChildren(ctx) +} diff --git a/pkg/parser/havingexpression/grammar/havingexpression_lexer.go b/pkg/parser/havingexpression/grammar/havingexpression_lexer.go new file mode 100644 index 000000000..756374f07 --- /dev/null +++ b/pkg/parser/havingexpression/grammar/havingexpression_lexer.go @@ -0,0 +1,232 @@ +// Code generated from grammar/HavingExpression.g4 by ANTLR 4.13.2. DO NOT EDIT. + +package parser + +import ( + "fmt" + "github.com/antlr4-go/antlr/v4" + "sync" + "unicode" +) + +// Suppress unused import error +var _ = fmt.Printf +var _ = sync.Once{} +var _ = unicode.IsLetter + +type HavingExpressionLexer struct { + *antlr.BaseLexer + channelNames []string + modeNames []string + // TODO: EOF string +} + +var HavingExpressionLexerLexerStaticData struct { + once sync.Once + serializedATN []int32 + ChannelNames []string + ModeNames []string + LiteralNames []string + SymbolicNames []string + RuleNames []string + PredictionContextCache *antlr.PredictionContextCache + atn *antlr.ATN + decisionToDFA []*antlr.DFA +} + +func havingexpressionlexerLexerInit() { + staticData := &HavingExpressionLexerLexerStaticData + staticData.ChannelNames = []string{ + "DEFAULT_TOKEN_CHANNEL", "HIDDEN", + } + staticData.ModeNames = []string{ + "DEFAULT_MODE", + } + staticData.LiteralNames = []string{ + "", "'('", "')'", "'['", "']'", "','", "", "'!='", "'<>'", "'<'", "'<='", + "'>'", "'>='", "'+'", "'-'", "'*'", "'/'", "'%'", + } + staticData.SymbolicNames = []string{ + "", "LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS", + "NEQ", "LT", "LE", "GT", "GE", "PLUS", "MINUS", "STAR", "SLASH", "PERCENT", + "NOT", "AND", "OR", "IN", "BOOL", "NUMBER", "IDENTIFIER", "STRING", + "WS", + } + staticData.RuleNames = []string{ + "LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS", + "NEQ", "LT", "LE", "GT", "GE", "PLUS", "MINUS", "STAR", "SLASH", "PERCENT", + "NOT", "AND", "OR", "IN", "BOOL", "SIGN", "NUMBER", "IDENTIFIER", "STRING", + "WS", "DIGIT", + } + staticData.PredictionContextCache = antlr.NewPredictionContextCache() + staticData.serializedATN = []int32{ + 4, 0, 26, 216, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, + 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, + 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, + 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, + 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, + 2, 26, 7, 26, 2, 27, 7, 27, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, + 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 71, 8, 5, 1, 6, 1, 6, 1, 6, 1, 7, + 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, + 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, + 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, + 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, + 1, 21, 1, 21, 3, 21, 122, 8, 21, 1, 22, 1, 22, 1, 23, 4, 23, 127, 8, 23, + 11, 23, 12, 23, 128, 1, 23, 1, 23, 5, 23, 133, 8, 23, 10, 23, 12, 23, 136, + 9, 23, 3, 23, 138, 8, 23, 1, 23, 1, 23, 3, 23, 142, 8, 23, 1, 23, 4, 23, + 145, 8, 23, 11, 23, 12, 23, 146, 3, 23, 149, 8, 23, 1, 23, 1, 23, 4, 23, + 153, 8, 23, 11, 23, 12, 23, 154, 1, 23, 1, 23, 3, 23, 159, 8, 23, 1, 23, + 4, 23, 162, 8, 23, 11, 23, 12, 23, 163, 3, 23, 166, 8, 23, 3, 23, 168, + 8, 23, 1, 24, 1, 24, 5, 24, 172, 8, 24, 10, 24, 12, 24, 175, 9, 24, 1, + 24, 1, 24, 1, 24, 5, 24, 180, 8, 24, 10, 24, 12, 24, 183, 9, 24, 5, 24, + 185, 8, 24, 10, 24, 12, 24, 188, 9, 24, 1, 25, 1, 25, 5, 25, 192, 8, 25, + 10, 25, 12, 25, 195, 9, 25, 1, 25, 1, 25, 1, 25, 5, 25, 200, 8, 25, 10, + 25, 12, 25, 203, 9, 25, 1, 25, 3, 25, 206, 8, 25, 1, 26, 4, 26, 209, 8, + 26, 11, 26, 12, 26, 210, 1, 26, 1, 26, 1, 27, 1, 27, 0, 0, 28, 1, 1, 3, + 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, + 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, + 43, 22, 45, 0, 47, 23, 49, 24, 51, 25, 53, 26, 55, 0, 1, 0, 19, 2, 0, 78, + 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0, 84, 84, 116, 116, 2, 0, 65, + 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 82, 82, 114, 114, 2, 0, 73, 73, + 105, 105, 2, 0, 85, 85, 117, 117, 2, 0, 69, 69, 101, 101, 2, 0, 70, 70, + 102, 102, 2, 0, 76, 76, 108, 108, 2, 0, 83, 83, 115, 115, 2, 0, 43, 43, + 45, 45, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, + 122, 1, 0, 39, 39, 1, 0, 34, 34, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, + 57, 233, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, + 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, + 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, + 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, + 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, + 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 47, 1, 0, + 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 1, 57, 1, + 0, 0, 0, 3, 59, 1, 0, 0, 0, 5, 61, 1, 0, 0, 0, 7, 63, 1, 0, 0, 0, 9, 65, + 1, 0, 0, 0, 11, 70, 1, 0, 0, 0, 13, 72, 1, 0, 0, 0, 15, 75, 1, 0, 0, 0, + 17, 78, 1, 0, 0, 0, 19, 80, 1, 0, 0, 0, 21, 83, 1, 0, 0, 0, 23, 85, 1, + 0, 0, 0, 25, 88, 1, 0, 0, 0, 27, 90, 1, 0, 0, 0, 29, 92, 1, 0, 0, 0, 31, + 94, 1, 0, 0, 0, 33, 96, 1, 0, 0, 0, 35, 98, 1, 0, 0, 0, 37, 102, 1, 0, + 0, 0, 39, 106, 1, 0, 0, 0, 41, 109, 1, 0, 0, 0, 43, 121, 1, 0, 0, 0, 45, + 123, 1, 0, 0, 0, 47, 167, 1, 0, 0, 0, 49, 169, 1, 0, 0, 0, 51, 205, 1, + 0, 0, 0, 53, 208, 1, 0, 0, 0, 55, 214, 1, 0, 0, 0, 57, 58, 5, 40, 0, 0, + 58, 2, 1, 0, 0, 0, 59, 60, 5, 41, 0, 0, 60, 4, 1, 0, 0, 0, 61, 62, 5, 91, + 0, 0, 62, 6, 1, 0, 0, 0, 63, 64, 5, 93, 0, 0, 64, 8, 1, 0, 0, 0, 65, 66, + 5, 44, 0, 0, 66, 10, 1, 0, 0, 0, 67, 71, 5, 61, 0, 0, 68, 69, 5, 61, 0, + 0, 69, 71, 5, 61, 0, 0, 70, 67, 1, 0, 0, 0, 70, 68, 1, 0, 0, 0, 71, 12, + 1, 0, 0, 0, 72, 73, 5, 33, 0, 0, 73, 74, 5, 61, 0, 0, 74, 14, 1, 0, 0, + 0, 75, 76, 5, 60, 0, 0, 76, 77, 5, 62, 0, 0, 77, 16, 1, 0, 0, 0, 78, 79, + 5, 60, 0, 0, 79, 18, 1, 0, 0, 0, 80, 81, 5, 60, 0, 0, 81, 82, 5, 61, 0, + 0, 82, 20, 1, 0, 0, 0, 83, 84, 5, 62, 0, 0, 84, 22, 1, 0, 0, 0, 85, 86, + 5, 62, 0, 0, 86, 87, 5, 61, 0, 0, 87, 24, 1, 0, 0, 0, 88, 89, 5, 43, 0, + 0, 89, 26, 1, 0, 0, 0, 90, 91, 5, 45, 0, 0, 91, 28, 1, 0, 0, 0, 92, 93, + 5, 42, 0, 0, 93, 30, 1, 0, 0, 0, 94, 95, 5, 47, 0, 0, 95, 32, 1, 0, 0, + 0, 96, 97, 5, 37, 0, 0, 97, 34, 1, 0, 0, 0, 98, 99, 7, 0, 0, 0, 99, 100, + 7, 1, 0, 0, 100, 101, 7, 2, 0, 0, 101, 36, 1, 0, 0, 0, 102, 103, 7, 3, + 0, 0, 103, 104, 7, 0, 0, 0, 104, 105, 7, 4, 0, 0, 105, 38, 1, 0, 0, 0, + 106, 107, 7, 1, 0, 0, 107, 108, 7, 5, 0, 0, 108, 40, 1, 0, 0, 0, 109, 110, + 7, 6, 0, 0, 110, 111, 7, 0, 0, 0, 111, 42, 1, 0, 0, 0, 112, 113, 7, 2, + 0, 0, 113, 114, 7, 5, 0, 0, 114, 115, 7, 7, 0, 0, 115, 122, 7, 8, 0, 0, + 116, 117, 7, 9, 0, 0, 117, 118, 7, 3, 0, 0, 118, 119, 7, 10, 0, 0, 119, + 120, 7, 11, 0, 0, 120, 122, 7, 8, 0, 0, 121, 112, 1, 0, 0, 0, 121, 116, + 1, 0, 0, 0, 122, 44, 1, 0, 0, 0, 123, 124, 7, 12, 0, 0, 124, 46, 1, 0, + 0, 0, 125, 127, 3, 55, 27, 0, 126, 125, 1, 0, 0, 0, 127, 128, 1, 0, 0, + 0, 128, 126, 1, 0, 0, 0, 128, 129, 1, 0, 0, 0, 129, 137, 1, 0, 0, 0, 130, + 134, 5, 46, 0, 0, 131, 133, 3, 55, 27, 0, 132, 131, 1, 0, 0, 0, 133, 136, + 1, 0, 0, 0, 134, 132, 1, 0, 0, 0, 134, 135, 1, 0, 0, 0, 135, 138, 1, 0, + 0, 0, 136, 134, 1, 0, 0, 0, 137, 130, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, + 138, 148, 1, 0, 0, 0, 139, 141, 7, 8, 0, 0, 140, 142, 3, 45, 22, 0, 141, + 140, 1, 0, 0, 0, 141, 142, 1, 0, 0, 0, 142, 144, 1, 0, 0, 0, 143, 145, + 3, 55, 27, 0, 144, 143, 1, 0, 0, 0, 145, 146, 1, 0, 0, 0, 146, 144, 1, + 0, 0, 0, 146, 147, 1, 0, 0, 0, 147, 149, 1, 0, 0, 0, 148, 139, 1, 0, 0, + 0, 148, 149, 1, 0, 0, 0, 149, 168, 1, 0, 0, 0, 150, 152, 5, 46, 0, 0, 151, + 153, 3, 55, 27, 0, 152, 151, 1, 0, 0, 0, 153, 154, 1, 0, 0, 0, 154, 152, + 1, 0, 0, 0, 154, 155, 1, 0, 0, 0, 155, 165, 1, 0, 0, 0, 156, 158, 7, 8, + 0, 0, 157, 159, 3, 45, 22, 0, 158, 157, 1, 0, 0, 0, 158, 159, 1, 0, 0, + 0, 159, 161, 1, 0, 0, 0, 160, 162, 3, 55, 27, 0, 161, 160, 1, 0, 0, 0, + 162, 163, 1, 0, 0, 0, 163, 161, 1, 0, 0, 0, 163, 164, 1, 0, 0, 0, 164, + 166, 1, 0, 0, 0, 165, 156, 1, 0, 0, 0, 165, 166, 1, 0, 0, 0, 166, 168, + 1, 0, 0, 0, 167, 126, 1, 0, 0, 0, 167, 150, 1, 0, 0, 0, 168, 48, 1, 0, + 0, 0, 169, 173, 7, 13, 0, 0, 170, 172, 7, 14, 0, 0, 171, 170, 1, 0, 0, + 0, 172, 175, 1, 0, 0, 0, 173, 171, 1, 0, 0, 0, 173, 174, 1, 0, 0, 0, 174, + 186, 1, 0, 0, 0, 175, 173, 1, 0, 0, 0, 176, 177, 5, 46, 0, 0, 177, 181, + 7, 13, 0, 0, 178, 180, 7, 14, 0, 0, 179, 178, 1, 0, 0, 0, 180, 183, 1, + 0, 0, 0, 181, 179, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, 185, 1, 0, 0, + 0, 183, 181, 1, 0, 0, 0, 184, 176, 1, 0, 0, 0, 185, 188, 1, 0, 0, 0, 186, + 184, 1, 0, 0, 0, 186, 187, 1, 0, 0, 0, 187, 50, 1, 0, 0, 0, 188, 186, 1, + 0, 0, 0, 189, 193, 5, 39, 0, 0, 190, 192, 8, 15, 0, 0, 191, 190, 1, 0, + 0, 0, 192, 195, 1, 0, 0, 0, 193, 191, 1, 0, 0, 0, 193, 194, 1, 0, 0, 0, + 194, 196, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 196, 206, 5, 39, 0, 0, 197, + 201, 5, 34, 0, 0, 198, 200, 8, 16, 0, 0, 199, 198, 1, 0, 0, 0, 200, 203, + 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 204, 1, 0, + 0, 0, 203, 201, 1, 0, 0, 0, 204, 206, 5, 34, 0, 0, 205, 189, 1, 0, 0, 0, + 205, 197, 1, 0, 0, 0, 206, 52, 1, 0, 0, 0, 207, 209, 7, 17, 0, 0, 208, + 207, 1, 0, 0, 0, 209, 210, 1, 0, 0, 0, 210, 208, 1, 0, 0, 0, 210, 211, + 1, 0, 0, 0, 211, 212, 1, 0, 0, 0, 212, 213, 6, 26, 0, 0, 213, 54, 1, 0, + 0, 0, 214, 215, 7, 18, 0, 0, 215, 56, 1, 0, 0, 0, 21, 0, 70, 121, 128, + 134, 137, 141, 146, 148, 154, 158, 163, 165, 167, 173, 181, 186, 193, 201, + 205, 210, 1, 6, 0, 0, + } + deserializer := antlr.NewATNDeserializer(nil) + staticData.atn = deserializer.Deserialize(staticData.serializedATN) + atn := staticData.atn + staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState)) + decisionToDFA := staticData.decisionToDFA + for index, state := range atn.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(state, index) + } +} + +// HavingExpressionLexerInit initializes any static state used to implement HavingExpressionLexer. By default the +// static state used to implement the lexer is lazily initialized during the first call to +// NewHavingExpressionLexer(). You can call this function if you wish to initialize the static state ahead +// of time. +func HavingExpressionLexerInit() { + staticData := &HavingExpressionLexerLexerStaticData + staticData.once.Do(havingexpressionlexerLexerInit) +} + +// NewHavingExpressionLexer produces a new lexer instance for the optional input antlr.CharStream. +func NewHavingExpressionLexer(input antlr.CharStream) *HavingExpressionLexer { + HavingExpressionLexerInit() + l := new(HavingExpressionLexer) + l.BaseLexer = antlr.NewBaseLexer(input) + staticData := &HavingExpressionLexerLexerStaticData + l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache) + l.channelNames = staticData.ChannelNames + l.modeNames = staticData.ModeNames + l.RuleNames = staticData.RuleNames + l.LiteralNames = staticData.LiteralNames + l.SymbolicNames = staticData.SymbolicNames + l.GrammarFileName = "HavingExpression.g4" + // TODO: l.EOF = antlr.TokenEOF + + return l +} + +// HavingExpressionLexer tokens. +const ( + HavingExpressionLexerLPAREN = 1 + HavingExpressionLexerRPAREN = 2 + HavingExpressionLexerLBRACK = 3 + HavingExpressionLexerRBRACK = 4 + HavingExpressionLexerCOMMA = 5 + HavingExpressionLexerEQUALS = 6 + HavingExpressionLexerNOT_EQUALS = 7 + HavingExpressionLexerNEQ = 8 + HavingExpressionLexerLT = 9 + HavingExpressionLexerLE = 10 + HavingExpressionLexerGT = 11 + HavingExpressionLexerGE = 12 + HavingExpressionLexerPLUS = 13 + HavingExpressionLexerMINUS = 14 + HavingExpressionLexerSTAR = 15 + HavingExpressionLexerSLASH = 16 + HavingExpressionLexerPERCENT = 17 + HavingExpressionLexerNOT = 18 + HavingExpressionLexerAND = 19 + HavingExpressionLexerOR = 20 + HavingExpressionLexerIN = 21 + HavingExpressionLexerBOOL = 22 + HavingExpressionLexerNUMBER = 23 + HavingExpressionLexerIDENTIFIER = 24 + HavingExpressionLexerSTRING = 25 + HavingExpressionLexerWS = 26 +) diff --git a/pkg/parser/havingexpression/grammar/havingexpression_listener.go b/pkg/parser/havingexpression/grammar/havingexpression_listener.go new file mode 100644 index 000000000..036d42568 --- /dev/null +++ b/pkg/parser/havingexpression/grammar/havingexpression_listener.go @@ -0,0 +1,118 @@ +// Code generated from grammar/HavingExpression.g4 by ANTLR 4.13.2. DO NOT EDIT. + +package parser // HavingExpression + +import "github.com/antlr4-go/antlr/v4" + +// HavingExpressionListener is a complete listener for a parse tree produced by HavingExpressionParser. +type HavingExpressionListener interface { + antlr.ParseTreeListener + + // EnterQuery is called when entering the query production. + EnterQuery(c *QueryContext) + + // EnterExpression is called when entering the expression production. + EnterExpression(c *ExpressionContext) + + // EnterOrExpression is called when entering the orExpression production. + EnterOrExpression(c *OrExpressionContext) + + // EnterAndExpression is called when entering the andExpression production. + EnterAndExpression(c *AndExpressionContext) + + // EnterPrimary is called when entering the primary production. + EnterPrimary(c *PrimaryContext) + + // EnterComparison is called when entering the comparison production. + EnterComparison(c *ComparisonContext) + + // EnterCompOp is called when entering the compOp production. + EnterCompOp(c *CompOpContext) + + // EnterInList is called when entering the inList production. + EnterInList(c *InListContext) + + // EnterSignedNumber is called when entering the signedNumber production. + EnterSignedNumber(c *SignedNumberContext) + + // EnterOperand is called when entering the operand production. + EnterOperand(c *OperandContext) + + // EnterTerm is called when entering the term production. + EnterTerm(c *TermContext) + + // EnterFactor is called when entering the factor production. + EnterFactor(c *FactorContext) + + // EnterAtom is called when entering the atom production. + EnterAtom(c *AtomContext) + + // EnterFunctionCall is called when entering the functionCall production. + EnterFunctionCall(c *FunctionCallContext) + + // EnterFunctionArgList is called when entering the functionArgList production. + EnterFunctionArgList(c *FunctionArgListContext) + + // EnterFuncArg is called when entering the funcArg production. + EnterFuncArg(c *FuncArgContext) + + // EnterFuncArgToken is called when entering the funcArgToken production. + EnterFuncArgToken(c *FuncArgTokenContext) + + // EnterIdentifier is called when entering the identifier production. + EnterIdentifier(c *IdentifierContext) + + // ExitQuery is called when exiting the query production. + ExitQuery(c *QueryContext) + + // ExitExpression is called when exiting the expression production. + ExitExpression(c *ExpressionContext) + + // ExitOrExpression is called when exiting the orExpression production. + ExitOrExpression(c *OrExpressionContext) + + // ExitAndExpression is called when exiting the andExpression production. + ExitAndExpression(c *AndExpressionContext) + + // ExitPrimary is called when exiting the primary production. + ExitPrimary(c *PrimaryContext) + + // ExitComparison is called when exiting the comparison production. + ExitComparison(c *ComparisonContext) + + // ExitCompOp is called when exiting the compOp production. + ExitCompOp(c *CompOpContext) + + // ExitInList is called when exiting the inList production. + ExitInList(c *InListContext) + + // ExitSignedNumber is called when exiting the signedNumber production. + ExitSignedNumber(c *SignedNumberContext) + + // ExitOperand is called when exiting the operand production. + ExitOperand(c *OperandContext) + + // ExitTerm is called when exiting the term production. + ExitTerm(c *TermContext) + + // ExitFactor is called when exiting the factor production. + ExitFactor(c *FactorContext) + + // ExitAtom is called when exiting the atom production. + ExitAtom(c *AtomContext) + + // ExitFunctionCall is called when exiting the functionCall production. + ExitFunctionCall(c *FunctionCallContext) + + // ExitFunctionArgList is called when exiting the functionArgList production. + ExitFunctionArgList(c *FunctionArgListContext) + + // ExitFuncArg is called when exiting the funcArg production. + ExitFuncArg(c *FuncArgContext) + + // ExitFuncArgToken is called when exiting the funcArgToken production. + ExitFuncArgToken(c *FuncArgTokenContext) + + // ExitIdentifier is called when exiting the identifier production. + ExitIdentifier(c *IdentifierContext) +} diff --git a/pkg/parser/havingexpression/grammar/havingexpression_parser.go b/pkg/parser/havingexpression/grammar/havingexpression_parser.go new file mode 100644 index 000000000..2de4f7052 --- /dev/null +++ b/pkg/parser/havingexpression/grammar/havingexpression_parser.go @@ -0,0 +1,3834 @@ +// Code generated from grammar/HavingExpression.g4 by ANTLR 4.13.2. DO NOT EDIT. + +package parser // HavingExpression + +import ( + "fmt" + "strconv" + "sync" + + "github.com/antlr4-go/antlr/v4" +) + +// Suppress unused import errors +var _ = fmt.Printf +var _ = strconv.Itoa +var _ = sync.Once{} + +type HavingExpressionParser struct { + *antlr.BaseParser +} + +var HavingExpressionParserStaticData struct { + once sync.Once + serializedATN []int32 + LiteralNames []string + SymbolicNames []string + RuleNames []string + PredictionContextCache *antlr.PredictionContextCache + atn *antlr.ATN + decisionToDFA []*antlr.DFA +} + +func havingexpressionParserInit() { + staticData := &HavingExpressionParserStaticData + staticData.LiteralNames = []string{ + "", "'('", "')'", "'['", "']'", "','", "", "'!='", "'<>'", "'<'", "'<='", + "'>'", "'>='", "'+'", "'-'", "'*'", "'/'", "'%'", + } + staticData.SymbolicNames = []string{ + "", "LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS", + "NEQ", "LT", "LE", "GT", "GE", "PLUS", "MINUS", "STAR", "SLASH", "PERCENT", + "NOT", "AND", "OR", "IN", "BOOL", "NUMBER", "IDENTIFIER", "STRING", + "WS", + } + staticData.RuleNames = []string{ + "query", "expression", "orExpression", "andExpression", "primary", "comparison", + "compOp", "inList", "signedNumber", "operand", "term", "factor", "atom", + "functionCall", "functionArgList", "funcArg", "funcArgToken", "identifier", + } + staticData.PredictionContextCache = antlr.NewPredictionContextCache() + staticData.serializedATN = []int32{ + 4, 1, 26, 199, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, + 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, + 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, + 2, 16, 7, 16, 2, 17, 7, 17, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, + 2, 5, 2, 45, 8, 2, 10, 2, 12, 2, 48, 9, 2, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, + 54, 8, 3, 10, 3, 12, 3, 57, 9, 3, 1, 4, 3, 4, 60, 8, 4, 1, 4, 1, 4, 1, + 4, 1, 4, 1, 4, 3, 4, 67, 8, 4, 1, 4, 3, 4, 70, 8, 4, 1, 5, 1, 5, 1, 5, + 1, 5, 1, 5, 1, 5, 3, 5, 78, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, + 5, 3, 5, 87, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 94, 8, 5, 1, 6, + 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 101, 8, 7, 10, 7, 12, 7, 104, 9, 7, 1, 8, + 3, 8, 107, 8, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, + 117, 8, 9, 10, 9, 12, 9, 120, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, + 1, 10, 5, 10, 128, 8, 10, 10, 10, 12, 10, 131, 9, 10, 1, 11, 1, 11, 1, + 11, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 140, 8, 11, 1, 12, 1, 12, 1, 12, + 1, 12, 3, 12, 146, 8, 12, 1, 13, 1, 13, 1, 13, 3, 13, 151, 8, 13, 1, 13, + 1, 13, 1, 14, 1, 14, 1, 14, 5, 14, 158, 8, 14, 10, 14, 12, 14, 161, 9, + 14, 1, 15, 4, 15, 164, 8, 15, 11, 15, 12, 15, 165, 1, 16, 1, 16, 1, 16, + 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, + 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 5, 16, 189, 8, 16, + 10, 16, 12, 16, 192, 9, 16, 1, 16, 3, 16, 195, 8, 16, 1, 17, 1, 17, 1, + 17, 0, 2, 18, 20, 18, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, + 28, 30, 32, 34, 0, 3, 1, 0, 6, 12, 1, 0, 13, 14, 1, 0, 15, 17, 222, 0, + 36, 1, 0, 0, 0, 2, 39, 1, 0, 0, 0, 4, 41, 1, 0, 0, 0, 6, 49, 1, 0, 0, 0, + 8, 69, 1, 0, 0, 0, 10, 93, 1, 0, 0, 0, 12, 95, 1, 0, 0, 0, 14, 97, 1, 0, + 0, 0, 16, 106, 1, 0, 0, 0, 18, 110, 1, 0, 0, 0, 20, 121, 1, 0, 0, 0, 22, + 139, 1, 0, 0, 0, 24, 145, 1, 0, 0, 0, 26, 147, 1, 0, 0, 0, 28, 154, 1, + 0, 0, 0, 30, 163, 1, 0, 0, 0, 32, 194, 1, 0, 0, 0, 34, 196, 1, 0, 0, 0, + 36, 37, 3, 2, 1, 0, 37, 38, 5, 0, 0, 1, 38, 1, 1, 0, 0, 0, 39, 40, 3, 4, + 2, 0, 40, 3, 1, 0, 0, 0, 41, 46, 3, 6, 3, 0, 42, 43, 5, 20, 0, 0, 43, 45, + 3, 6, 3, 0, 44, 42, 1, 0, 0, 0, 45, 48, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, + 46, 47, 1, 0, 0, 0, 47, 5, 1, 0, 0, 0, 48, 46, 1, 0, 0, 0, 49, 55, 3, 8, + 4, 0, 50, 51, 5, 19, 0, 0, 51, 54, 3, 8, 4, 0, 52, 54, 3, 8, 4, 0, 53, + 50, 1, 0, 0, 0, 53, 52, 1, 0, 0, 0, 54, 57, 1, 0, 0, 0, 55, 53, 1, 0, 0, + 0, 55, 56, 1, 0, 0, 0, 56, 7, 1, 0, 0, 0, 57, 55, 1, 0, 0, 0, 58, 60, 5, + 18, 0, 0, 59, 58, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 61, 1, 0, 0, 0, 61, + 62, 5, 1, 0, 0, 62, 63, 3, 4, 2, 0, 63, 64, 5, 2, 0, 0, 64, 70, 1, 0, 0, + 0, 65, 67, 5, 18, 0, 0, 66, 65, 1, 0, 0, 0, 66, 67, 1, 0, 0, 0, 67, 68, + 1, 0, 0, 0, 68, 70, 3, 10, 5, 0, 69, 59, 1, 0, 0, 0, 69, 66, 1, 0, 0, 0, + 70, 9, 1, 0, 0, 0, 71, 72, 3, 18, 9, 0, 72, 73, 3, 12, 6, 0, 73, 74, 3, + 18, 9, 0, 74, 94, 1, 0, 0, 0, 75, 77, 3, 18, 9, 0, 76, 78, 5, 18, 0, 0, + 77, 76, 1, 0, 0, 0, 77, 78, 1, 0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 80, 5, + 21, 0, 0, 80, 81, 5, 1, 0, 0, 81, 82, 3, 14, 7, 0, 82, 83, 5, 2, 0, 0, + 83, 94, 1, 0, 0, 0, 84, 86, 3, 18, 9, 0, 85, 87, 5, 18, 0, 0, 86, 85, 1, + 0, 0, 0, 86, 87, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 89, 5, 21, 0, 0, 89, + 90, 5, 3, 0, 0, 90, 91, 3, 14, 7, 0, 91, 92, 5, 4, 0, 0, 92, 94, 1, 0, + 0, 0, 93, 71, 1, 0, 0, 0, 93, 75, 1, 0, 0, 0, 93, 84, 1, 0, 0, 0, 94, 11, + 1, 0, 0, 0, 95, 96, 7, 0, 0, 0, 96, 13, 1, 0, 0, 0, 97, 102, 3, 16, 8, + 0, 98, 99, 5, 5, 0, 0, 99, 101, 3, 16, 8, 0, 100, 98, 1, 0, 0, 0, 101, + 104, 1, 0, 0, 0, 102, 100, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 15, 1, + 0, 0, 0, 104, 102, 1, 0, 0, 0, 105, 107, 7, 1, 0, 0, 106, 105, 1, 0, 0, + 0, 106, 107, 1, 0, 0, 0, 107, 108, 1, 0, 0, 0, 108, 109, 5, 23, 0, 0, 109, + 17, 1, 0, 0, 0, 110, 111, 6, 9, -1, 0, 111, 112, 3, 20, 10, 0, 112, 118, + 1, 0, 0, 0, 113, 114, 10, 2, 0, 0, 114, 115, 7, 1, 0, 0, 115, 117, 3, 20, + 10, 0, 116, 113, 1, 0, 0, 0, 117, 120, 1, 0, 0, 0, 118, 116, 1, 0, 0, 0, + 118, 119, 1, 0, 0, 0, 119, 19, 1, 0, 0, 0, 120, 118, 1, 0, 0, 0, 121, 122, + 6, 10, -1, 0, 122, 123, 3, 22, 11, 0, 123, 129, 1, 0, 0, 0, 124, 125, 10, + 2, 0, 0, 125, 126, 7, 2, 0, 0, 126, 128, 3, 22, 11, 0, 127, 124, 1, 0, + 0, 0, 128, 131, 1, 0, 0, 0, 129, 127, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, + 130, 21, 1, 0, 0, 0, 131, 129, 1, 0, 0, 0, 132, 133, 7, 1, 0, 0, 133, 140, + 3, 22, 11, 0, 134, 135, 5, 1, 0, 0, 135, 136, 3, 18, 9, 0, 136, 137, 5, + 2, 0, 0, 137, 140, 1, 0, 0, 0, 138, 140, 3, 24, 12, 0, 139, 132, 1, 0, + 0, 0, 139, 134, 1, 0, 0, 0, 139, 138, 1, 0, 0, 0, 140, 23, 1, 0, 0, 0, + 141, 146, 3, 26, 13, 0, 142, 146, 3, 34, 17, 0, 143, 146, 5, 23, 0, 0, + 144, 146, 5, 25, 0, 0, 145, 141, 1, 0, 0, 0, 145, 142, 1, 0, 0, 0, 145, + 143, 1, 0, 0, 0, 145, 144, 1, 0, 0, 0, 146, 25, 1, 0, 0, 0, 147, 148, 5, + 24, 0, 0, 148, 150, 5, 1, 0, 0, 149, 151, 3, 28, 14, 0, 150, 149, 1, 0, + 0, 0, 150, 151, 1, 0, 0, 0, 151, 152, 1, 0, 0, 0, 152, 153, 5, 2, 0, 0, + 153, 27, 1, 0, 0, 0, 154, 159, 3, 30, 15, 0, 155, 156, 5, 5, 0, 0, 156, + 158, 3, 30, 15, 0, 157, 155, 1, 0, 0, 0, 158, 161, 1, 0, 0, 0, 159, 157, + 1, 0, 0, 0, 159, 160, 1, 0, 0, 0, 160, 29, 1, 0, 0, 0, 161, 159, 1, 0, + 0, 0, 162, 164, 3, 32, 16, 0, 163, 162, 1, 0, 0, 0, 164, 165, 1, 0, 0, + 0, 165, 163, 1, 0, 0, 0, 165, 166, 1, 0, 0, 0, 166, 31, 1, 0, 0, 0, 167, + 195, 5, 24, 0, 0, 168, 195, 5, 25, 0, 0, 169, 195, 5, 23, 0, 0, 170, 195, + 5, 22, 0, 0, 171, 195, 5, 6, 0, 0, 172, 195, 5, 7, 0, 0, 173, 195, 5, 8, + 0, 0, 174, 195, 5, 9, 0, 0, 175, 195, 5, 10, 0, 0, 176, 195, 5, 11, 0, + 0, 177, 195, 5, 12, 0, 0, 178, 195, 5, 13, 0, 0, 179, 195, 5, 14, 0, 0, + 180, 195, 5, 15, 0, 0, 181, 195, 5, 16, 0, 0, 182, 195, 5, 17, 0, 0, 183, + 195, 5, 18, 0, 0, 184, 195, 5, 19, 0, 0, 185, 195, 5, 20, 0, 0, 186, 190, + 5, 1, 0, 0, 187, 189, 3, 32, 16, 0, 188, 187, 1, 0, 0, 0, 189, 192, 1, + 0, 0, 0, 190, 188, 1, 0, 0, 0, 190, 191, 1, 0, 0, 0, 191, 193, 1, 0, 0, + 0, 192, 190, 1, 0, 0, 0, 193, 195, 5, 2, 0, 0, 194, 167, 1, 0, 0, 0, 194, + 168, 1, 0, 0, 0, 194, 169, 1, 0, 0, 0, 194, 170, 1, 0, 0, 0, 194, 171, + 1, 0, 0, 0, 194, 172, 1, 0, 0, 0, 194, 173, 1, 0, 0, 0, 194, 174, 1, 0, + 0, 0, 194, 175, 1, 0, 0, 0, 194, 176, 1, 0, 0, 0, 194, 177, 1, 0, 0, 0, + 194, 178, 1, 0, 0, 0, 194, 179, 1, 0, 0, 0, 194, 180, 1, 0, 0, 0, 194, + 181, 1, 0, 0, 0, 194, 182, 1, 0, 0, 0, 194, 183, 1, 0, 0, 0, 194, 184, + 1, 0, 0, 0, 194, 185, 1, 0, 0, 0, 194, 186, 1, 0, 0, 0, 195, 33, 1, 0, + 0, 0, 196, 197, 5, 24, 0, 0, 197, 35, 1, 0, 0, 0, 20, 46, 53, 55, 59, 66, + 69, 77, 86, 93, 102, 106, 118, 129, 139, 145, 150, 159, 165, 190, 194, + } + deserializer := antlr.NewATNDeserializer(nil) + staticData.atn = deserializer.Deserialize(staticData.serializedATN) + atn := staticData.atn + staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState)) + decisionToDFA := staticData.decisionToDFA + for index, state := range atn.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(state, index) + } +} + +// HavingExpressionParserInit initializes any static state used to implement HavingExpressionParser. By default the +// static state used to implement the parser is lazily initialized during the first call to +// NewHavingExpressionParser(). You can call this function if you wish to initialize the static state ahead +// of time. +func HavingExpressionParserInit() { + staticData := &HavingExpressionParserStaticData + staticData.once.Do(havingexpressionParserInit) +} + +// NewHavingExpressionParser produces a new parser instance for the optional input antlr.TokenStream. +func NewHavingExpressionParser(input antlr.TokenStream) *HavingExpressionParser { + HavingExpressionParserInit() + this := new(HavingExpressionParser) + this.BaseParser = antlr.NewBaseParser(input) + staticData := &HavingExpressionParserStaticData + this.Interpreter = antlr.NewParserATNSimulator(this, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache) + this.RuleNames = staticData.RuleNames + this.LiteralNames = staticData.LiteralNames + this.SymbolicNames = staticData.SymbolicNames + this.GrammarFileName = "HavingExpression.g4" + + return this +} + +// HavingExpressionParser tokens. +const ( + HavingExpressionParserEOF = antlr.TokenEOF + HavingExpressionParserLPAREN = 1 + HavingExpressionParserRPAREN = 2 + HavingExpressionParserLBRACK = 3 + HavingExpressionParserRBRACK = 4 + HavingExpressionParserCOMMA = 5 + HavingExpressionParserEQUALS = 6 + HavingExpressionParserNOT_EQUALS = 7 + HavingExpressionParserNEQ = 8 + HavingExpressionParserLT = 9 + HavingExpressionParserLE = 10 + HavingExpressionParserGT = 11 + HavingExpressionParserGE = 12 + HavingExpressionParserPLUS = 13 + HavingExpressionParserMINUS = 14 + HavingExpressionParserSTAR = 15 + HavingExpressionParserSLASH = 16 + HavingExpressionParserPERCENT = 17 + HavingExpressionParserNOT = 18 + HavingExpressionParserAND = 19 + HavingExpressionParserOR = 20 + HavingExpressionParserIN = 21 + HavingExpressionParserBOOL = 22 + HavingExpressionParserNUMBER = 23 + HavingExpressionParserIDENTIFIER = 24 + HavingExpressionParserSTRING = 25 + HavingExpressionParserWS = 26 +) + +// HavingExpressionParser rules. +const ( + HavingExpressionParserRULE_query = 0 + HavingExpressionParserRULE_expression = 1 + HavingExpressionParserRULE_orExpression = 2 + HavingExpressionParserRULE_andExpression = 3 + HavingExpressionParserRULE_primary = 4 + HavingExpressionParserRULE_comparison = 5 + HavingExpressionParserRULE_compOp = 6 + HavingExpressionParserRULE_inList = 7 + HavingExpressionParserRULE_signedNumber = 8 + HavingExpressionParserRULE_operand = 9 + HavingExpressionParserRULE_term = 10 + HavingExpressionParserRULE_factor = 11 + HavingExpressionParserRULE_atom = 12 + HavingExpressionParserRULE_functionCall = 13 + HavingExpressionParserRULE_functionArgList = 14 + HavingExpressionParserRULE_funcArg = 15 + HavingExpressionParserRULE_funcArgToken = 16 + HavingExpressionParserRULE_identifier = 17 +) + +// IQueryContext is an interface to support dynamic dispatch. +type IQueryContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + Expression() IExpressionContext + EOF() antlr.TerminalNode + + // IsQueryContext differentiates from other interfaces. + IsQueryContext() +} + +type QueryContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyQueryContext() *QueryContext { + var p = new(QueryContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_query + return p +} + +func InitEmptyQueryContext(p *QueryContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_query +} + +func (*QueryContext) IsQueryContext() {} + +func NewQueryContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *QueryContext { + var p = new(QueryContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_query + + return p +} + +func (s *QueryContext) GetParser() antlr.Parser { return s.parser } + +func (s *QueryContext) Expression() IExpressionContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IExpressionContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IExpressionContext) +} + +func (s *QueryContext) EOF() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserEOF, 0) +} + +func (s *QueryContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *QueryContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *QueryContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterQuery(s) + } +} + +func (s *QueryContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitQuery(s) + } +} + +func (s *QueryContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitQuery(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) Query() (localctx IQueryContext) { + localctx = NewQueryContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 0, HavingExpressionParserRULE_query) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(36) + p.Expression() + } + { + p.SetState(37) + p.Match(HavingExpressionParserEOF) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IExpressionContext is an interface to support dynamic dispatch. +type IExpressionContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + OrExpression() IOrExpressionContext + + // IsExpressionContext differentiates from other interfaces. + IsExpressionContext() +} + +type ExpressionContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyExpressionContext() *ExpressionContext { + var p = new(ExpressionContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_expression + return p +} + +func InitEmptyExpressionContext(p *ExpressionContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_expression +} + +func (*ExpressionContext) IsExpressionContext() {} + +func NewExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExpressionContext { + var p = new(ExpressionContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_expression + + return p +} + +func (s *ExpressionContext) GetParser() antlr.Parser { return s.parser } + +func (s *ExpressionContext) OrExpression() IOrExpressionContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IOrExpressionContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IOrExpressionContext) +} + +func (s *ExpressionContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ExpressionContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterExpression(s) + } +} + +func (s *ExpressionContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitExpression(s) + } +} + +func (s *ExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitExpression(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) Expression() (localctx IExpressionContext) { + localctx = NewExpressionContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 2, HavingExpressionParserRULE_expression) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(39) + p.OrExpression() + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IOrExpressionContext is an interface to support dynamic dispatch. +type IOrExpressionContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllAndExpression() []IAndExpressionContext + AndExpression(i int) IAndExpressionContext + AllOR() []antlr.TerminalNode + OR(i int) antlr.TerminalNode + + // IsOrExpressionContext differentiates from other interfaces. + IsOrExpressionContext() +} + +type OrExpressionContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyOrExpressionContext() *OrExpressionContext { + var p = new(OrExpressionContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_orExpression + return p +} + +func InitEmptyOrExpressionContext(p *OrExpressionContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_orExpression +} + +func (*OrExpressionContext) IsOrExpressionContext() {} + +func NewOrExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *OrExpressionContext { + var p = new(OrExpressionContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_orExpression + + return p +} + +func (s *OrExpressionContext) GetParser() antlr.Parser { return s.parser } + +func (s *OrExpressionContext) AllAndExpression() []IAndExpressionContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IAndExpressionContext); ok { + len++ + } + } + + tst := make([]IAndExpressionContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IAndExpressionContext); ok { + tst[i] = t.(IAndExpressionContext) + i++ + } + } + + return tst +} + +func (s *OrExpressionContext) AndExpression(i int) IAndExpressionContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IAndExpressionContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IAndExpressionContext) +} + +func (s *OrExpressionContext) AllOR() []antlr.TerminalNode { + return s.GetTokens(HavingExpressionParserOR) +} + +func (s *OrExpressionContext) OR(i int) antlr.TerminalNode { + return s.GetToken(HavingExpressionParserOR, i) +} + +func (s *OrExpressionContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *OrExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *OrExpressionContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterOrExpression(s) + } +} + +func (s *OrExpressionContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitOrExpression(s) + } +} + +func (s *OrExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitOrExpression(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) OrExpression() (localctx IOrExpressionContext) { + localctx = NewOrExpressionContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 4, HavingExpressionParserRULE_orExpression) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(41) + p.AndExpression() + } + p.SetState(46) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + for _la == HavingExpressionParserOR { + { + p.SetState(42) + p.Match(HavingExpressionParserOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(43) + p.AndExpression() + } + + p.SetState(48) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IAndExpressionContext is an interface to support dynamic dispatch. +type IAndExpressionContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllPrimary() []IPrimaryContext + Primary(i int) IPrimaryContext + AllAND() []antlr.TerminalNode + AND(i int) antlr.TerminalNode + + // IsAndExpressionContext differentiates from other interfaces. + IsAndExpressionContext() +} + +type AndExpressionContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyAndExpressionContext() *AndExpressionContext { + var p = new(AndExpressionContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_andExpression + return p +} + +func InitEmptyAndExpressionContext(p *AndExpressionContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_andExpression +} + +func (*AndExpressionContext) IsAndExpressionContext() {} + +func NewAndExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AndExpressionContext { + var p = new(AndExpressionContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_andExpression + + return p +} + +func (s *AndExpressionContext) GetParser() antlr.Parser { return s.parser } + +func (s *AndExpressionContext) AllPrimary() []IPrimaryContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IPrimaryContext); ok { + len++ + } + } + + tst := make([]IPrimaryContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IPrimaryContext); ok { + tst[i] = t.(IPrimaryContext) + i++ + } + } + + return tst +} + +func (s *AndExpressionContext) Primary(i int) IPrimaryContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IPrimaryContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IPrimaryContext) +} + +func (s *AndExpressionContext) AllAND() []antlr.TerminalNode { + return s.GetTokens(HavingExpressionParserAND) +} + +func (s *AndExpressionContext) AND(i int) antlr.TerminalNode { + return s.GetToken(HavingExpressionParserAND, i) +} + +func (s *AndExpressionContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *AndExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *AndExpressionContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterAndExpression(s) + } +} + +func (s *AndExpressionContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitAndExpression(s) + } +} + +func (s *AndExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitAndExpression(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) AndExpression() (localctx IAndExpressionContext) { + localctx = NewAndExpressionContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 6, HavingExpressionParserRULE_andExpression) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(49) + p.Primary() + } + p.SetState(55) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + for (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&59531266) != 0 { + p.SetState(53) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + + switch p.GetTokenStream().LA(1) { + case HavingExpressionParserAND: + { + p.SetState(50) + p.Match(HavingExpressionParserAND) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(51) + p.Primary() + } + + case HavingExpressionParserLPAREN, HavingExpressionParserPLUS, HavingExpressionParserMINUS, HavingExpressionParserNOT, HavingExpressionParserNUMBER, HavingExpressionParserIDENTIFIER, HavingExpressionParserSTRING: + { + p.SetState(52) + p.Primary() + } + + default: + p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + goto errorExit + } + + p.SetState(57) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IPrimaryContext is an interface to support dynamic dispatch. +type IPrimaryContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + LPAREN() antlr.TerminalNode + OrExpression() IOrExpressionContext + RPAREN() antlr.TerminalNode + NOT() antlr.TerminalNode + Comparison() IComparisonContext + + // IsPrimaryContext differentiates from other interfaces. + IsPrimaryContext() +} + +type PrimaryContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyPrimaryContext() *PrimaryContext { + var p = new(PrimaryContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_primary + return p +} + +func InitEmptyPrimaryContext(p *PrimaryContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_primary +} + +func (*PrimaryContext) IsPrimaryContext() {} + +func NewPrimaryContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *PrimaryContext { + var p = new(PrimaryContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_primary + + return p +} + +func (s *PrimaryContext) GetParser() antlr.Parser { return s.parser } + +func (s *PrimaryContext) LPAREN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserLPAREN, 0) +} + +func (s *PrimaryContext) OrExpression() IOrExpressionContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IOrExpressionContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IOrExpressionContext) +} + +func (s *PrimaryContext) RPAREN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserRPAREN, 0) +} + +func (s *PrimaryContext) NOT() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserNOT, 0) +} + +func (s *PrimaryContext) Comparison() IComparisonContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IComparisonContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IComparisonContext) +} + +func (s *PrimaryContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *PrimaryContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *PrimaryContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterPrimary(s) + } +} + +func (s *PrimaryContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitPrimary(s) + } +} + +func (s *PrimaryContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitPrimary(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) Primary() (localctx IPrimaryContext) { + localctx = NewPrimaryContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 8, HavingExpressionParserRULE_primary) + var _la int + + p.SetState(69) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + + switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext()) { + case 1: + p.EnterOuterAlt(localctx, 1) + p.SetState(59) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == HavingExpressionParserNOT { + { + p.SetState(58) + p.Match(HavingExpressionParserNOT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(61) + p.Match(HavingExpressionParserLPAREN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(62) + p.OrExpression() + } + { + p.SetState(63) + p.Match(HavingExpressionParserRPAREN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case 2: + p.EnterOuterAlt(localctx, 2) + p.SetState(66) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == HavingExpressionParserNOT { + { + p.SetState(65) + p.Match(HavingExpressionParserNOT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(68) + p.Comparison() + } + + case antlr.ATNInvalidAltNumber: + goto errorExit + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IComparisonContext is an interface to support dynamic dispatch. +type IComparisonContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllOperand() []IOperandContext + Operand(i int) IOperandContext + CompOp() ICompOpContext + IN() antlr.TerminalNode + LPAREN() antlr.TerminalNode + InList() IInListContext + RPAREN() antlr.TerminalNode + NOT() antlr.TerminalNode + LBRACK() antlr.TerminalNode + RBRACK() antlr.TerminalNode + + // IsComparisonContext differentiates from other interfaces. + IsComparisonContext() +} + +type ComparisonContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyComparisonContext() *ComparisonContext { + var p = new(ComparisonContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_comparison + return p +} + +func InitEmptyComparisonContext(p *ComparisonContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_comparison +} + +func (*ComparisonContext) IsComparisonContext() {} + +func NewComparisonContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ComparisonContext { + var p = new(ComparisonContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_comparison + + return p +} + +func (s *ComparisonContext) GetParser() antlr.Parser { return s.parser } + +func (s *ComparisonContext) AllOperand() []IOperandContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IOperandContext); ok { + len++ + } + } + + tst := make([]IOperandContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IOperandContext); ok { + tst[i] = t.(IOperandContext) + i++ + } + } + + return tst +} + +func (s *ComparisonContext) Operand(i int) IOperandContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IOperandContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IOperandContext) +} + +func (s *ComparisonContext) CompOp() ICompOpContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ICompOpContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ICompOpContext) +} + +func (s *ComparisonContext) IN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserIN, 0) +} + +func (s *ComparisonContext) LPAREN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserLPAREN, 0) +} + +func (s *ComparisonContext) InList() IInListContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IInListContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IInListContext) +} + +func (s *ComparisonContext) RPAREN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserRPAREN, 0) +} + +func (s *ComparisonContext) NOT() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserNOT, 0) +} + +func (s *ComparisonContext) LBRACK() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserLBRACK, 0) +} + +func (s *ComparisonContext) RBRACK() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserRBRACK, 0) +} + +func (s *ComparisonContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ComparisonContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ComparisonContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterComparison(s) + } +} + +func (s *ComparisonContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitComparison(s) + } +} + +func (s *ComparisonContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitComparison(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) Comparison() (localctx IComparisonContext) { + localctx = NewComparisonContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 10, HavingExpressionParserRULE_comparison) + var _la int + + p.SetState(93) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + + switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 8, p.GetParserRuleContext()) { + case 1: + p.EnterOuterAlt(localctx, 1) + { + p.SetState(71) + p.operand(0) + } + { + p.SetState(72) + p.CompOp() + } + { + p.SetState(73) + p.operand(0) + } + + case 2: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(75) + p.operand(0) + } + p.SetState(77) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == HavingExpressionParserNOT { + { + p.SetState(76) + p.Match(HavingExpressionParserNOT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(79) + p.Match(HavingExpressionParserIN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(80) + p.Match(HavingExpressionParserLPAREN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(81) + p.InList() + } + { + p.SetState(82) + p.Match(HavingExpressionParserRPAREN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case 3: + p.EnterOuterAlt(localctx, 3) + { + p.SetState(84) + p.operand(0) + } + p.SetState(86) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == HavingExpressionParserNOT { + { + p.SetState(85) + p.Match(HavingExpressionParserNOT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(88) + p.Match(HavingExpressionParserIN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(89) + p.Match(HavingExpressionParserLBRACK) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(90) + p.InList() + } + { + p.SetState(91) + p.Match(HavingExpressionParserRBRACK) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case antlr.ATNInvalidAltNumber: + goto errorExit + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// ICompOpContext is an interface to support dynamic dispatch. +type ICompOpContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + EQUALS() antlr.TerminalNode + NOT_EQUALS() antlr.TerminalNode + NEQ() antlr.TerminalNode + LT() antlr.TerminalNode + LE() antlr.TerminalNode + GT() antlr.TerminalNode + GE() antlr.TerminalNode + + // IsCompOpContext differentiates from other interfaces. + IsCompOpContext() +} + +type CompOpContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyCompOpContext() *CompOpContext { + var p = new(CompOpContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_compOp + return p +} + +func InitEmptyCompOpContext(p *CompOpContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_compOp +} + +func (*CompOpContext) IsCompOpContext() {} + +func NewCompOpContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *CompOpContext { + var p = new(CompOpContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_compOp + + return p +} + +func (s *CompOpContext) GetParser() antlr.Parser { return s.parser } + +func (s *CompOpContext) EQUALS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserEQUALS, 0) +} + +func (s *CompOpContext) NOT_EQUALS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserNOT_EQUALS, 0) +} + +func (s *CompOpContext) NEQ() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserNEQ, 0) +} + +func (s *CompOpContext) LT() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserLT, 0) +} + +func (s *CompOpContext) LE() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserLE, 0) +} + +func (s *CompOpContext) GT() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserGT, 0) +} + +func (s *CompOpContext) GE() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserGE, 0) +} + +func (s *CompOpContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *CompOpContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *CompOpContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterCompOp(s) + } +} + +func (s *CompOpContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitCompOp(s) + } +} + +func (s *CompOpContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitCompOp(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) CompOp() (localctx ICompOpContext) { + localctx = NewCompOpContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 12, HavingExpressionParserRULE_compOp) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(95) + _la = p.GetTokenStream().LA(1) + + if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&8128) != 0) { + p.GetErrorHandler().RecoverInline(p) + } else { + p.GetErrorHandler().ReportMatch(p) + p.Consume() + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IInListContext is an interface to support dynamic dispatch. +type IInListContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllSignedNumber() []ISignedNumberContext + SignedNumber(i int) ISignedNumberContext + AllCOMMA() []antlr.TerminalNode + COMMA(i int) antlr.TerminalNode + + // IsInListContext differentiates from other interfaces. + IsInListContext() +} + +type InListContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyInListContext() *InListContext { + var p = new(InListContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_inList + return p +} + +func InitEmptyInListContext(p *InListContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_inList +} + +func (*InListContext) IsInListContext() {} + +func NewInListContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *InListContext { + var p = new(InListContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_inList + + return p +} + +func (s *InListContext) GetParser() antlr.Parser { return s.parser } + +func (s *InListContext) AllSignedNumber() []ISignedNumberContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(ISignedNumberContext); ok { + len++ + } + } + + tst := make([]ISignedNumberContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(ISignedNumberContext); ok { + tst[i] = t.(ISignedNumberContext) + i++ + } + } + + return tst +} + +func (s *InListContext) SignedNumber(i int) ISignedNumberContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ISignedNumberContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(ISignedNumberContext) +} + +func (s *InListContext) AllCOMMA() []antlr.TerminalNode { + return s.GetTokens(HavingExpressionParserCOMMA) +} + +func (s *InListContext) COMMA(i int) antlr.TerminalNode { + return s.GetToken(HavingExpressionParserCOMMA, i) +} + +func (s *InListContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *InListContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *InListContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterInList(s) + } +} + +func (s *InListContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitInList(s) + } +} + +func (s *InListContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitInList(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) InList() (localctx IInListContext) { + localctx = NewInListContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 14, HavingExpressionParserRULE_inList) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(97) + p.SignedNumber() + } + p.SetState(102) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + for _la == HavingExpressionParserCOMMA { + { + p.SetState(98) + p.Match(HavingExpressionParserCOMMA) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(99) + p.SignedNumber() + } + + p.SetState(104) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// ISignedNumberContext is an interface to support dynamic dispatch. +type ISignedNumberContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + NUMBER() antlr.TerminalNode + PLUS() antlr.TerminalNode + MINUS() antlr.TerminalNode + + // IsSignedNumberContext differentiates from other interfaces. + IsSignedNumberContext() +} + +type SignedNumberContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptySignedNumberContext() *SignedNumberContext { + var p = new(SignedNumberContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_signedNumber + return p +} + +func InitEmptySignedNumberContext(p *SignedNumberContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_signedNumber +} + +func (*SignedNumberContext) IsSignedNumberContext() {} + +func NewSignedNumberContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *SignedNumberContext { + var p = new(SignedNumberContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_signedNumber + + return p +} + +func (s *SignedNumberContext) GetParser() antlr.Parser { return s.parser } + +func (s *SignedNumberContext) NUMBER() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserNUMBER, 0) +} + +func (s *SignedNumberContext) PLUS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserPLUS, 0) +} + +func (s *SignedNumberContext) MINUS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserMINUS, 0) +} + +func (s *SignedNumberContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *SignedNumberContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *SignedNumberContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterSignedNumber(s) + } +} + +func (s *SignedNumberContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitSignedNumber(s) + } +} + +func (s *SignedNumberContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitSignedNumber(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) SignedNumber() (localctx ISignedNumberContext) { + localctx = NewSignedNumberContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 16, HavingExpressionParserRULE_signedNumber) + var _la int + + p.EnterOuterAlt(localctx, 1) + p.SetState(106) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == HavingExpressionParserPLUS || _la == HavingExpressionParserMINUS { + { + p.SetState(105) + _la = p.GetTokenStream().LA(1) + + if !(_la == HavingExpressionParserPLUS || _la == HavingExpressionParserMINUS) { + p.GetErrorHandler().RecoverInline(p) + } else { + p.GetErrorHandler().ReportMatch(p) + p.Consume() + } + } + + } + { + p.SetState(108) + p.Match(HavingExpressionParserNUMBER) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IOperandContext is an interface to support dynamic dispatch. +type IOperandContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + Term() ITermContext + Operand() IOperandContext + PLUS() antlr.TerminalNode + MINUS() antlr.TerminalNode + + // IsOperandContext differentiates from other interfaces. + IsOperandContext() +} + +type OperandContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyOperandContext() *OperandContext { + var p = new(OperandContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_operand + return p +} + +func InitEmptyOperandContext(p *OperandContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_operand +} + +func (*OperandContext) IsOperandContext() {} + +func NewOperandContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *OperandContext { + var p = new(OperandContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_operand + + return p +} + +func (s *OperandContext) GetParser() antlr.Parser { return s.parser } + +func (s *OperandContext) Term() ITermContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ITermContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ITermContext) +} + +func (s *OperandContext) Operand() IOperandContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IOperandContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IOperandContext) +} + +func (s *OperandContext) PLUS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserPLUS, 0) +} + +func (s *OperandContext) MINUS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserMINUS, 0) +} + +func (s *OperandContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *OperandContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *OperandContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterOperand(s) + } +} + +func (s *OperandContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitOperand(s) + } +} + +func (s *OperandContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitOperand(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) Operand() (localctx IOperandContext) { + return p.operand(0) +} + +func (p *HavingExpressionParser) operand(_p int) (localctx IOperandContext) { + var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext() + + _parentState := p.GetState() + localctx = NewOperandContext(p, p.GetParserRuleContext(), _parentState) + var _prevctx IOperandContext = localctx + var _ antlr.ParserRuleContext = _prevctx // TODO: To prevent unused variable warning. + _startState := 18 + p.EnterRecursionRule(localctx, 18, HavingExpressionParserRULE_operand, _p) + var _la int + + var _alt int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(111) + p.term(0) + } + + p.GetParserRuleContext().SetStop(p.GetTokenStream().LT(-1)) + p.SetState(118) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 11, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + if _alt == 1 { + if p.GetParseListeners() != nil { + p.TriggerExitRuleEvent() + } + _prevctx = localctx + localctx = NewOperandContext(p, _parentctx, _parentState) + p.PushNewRecursionContext(localctx, _startState, HavingExpressionParserRULE_operand) + p.SetState(113) + + if !(p.Precpred(p.GetParserRuleContext(), 2)) { + p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 2)", "")) + goto errorExit + } + { + p.SetState(114) + _la = p.GetTokenStream().LA(1) + + if !(_la == HavingExpressionParserPLUS || _la == HavingExpressionParserMINUS) { + p.GetErrorHandler().RecoverInline(p) + } else { + p.GetErrorHandler().ReportMatch(p) + p.Consume() + } + } + { + p.SetState(115) + p.term(0) + } + + } + p.SetState(120) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 11, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.UnrollRecursionContexts(_parentctx) + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// ITermContext is an interface to support dynamic dispatch. +type ITermContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + Factor() IFactorContext + Term() ITermContext + STAR() antlr.TerminalNode + SLASH() antlr.TerminalNode + PERCENT() antlr.TerminalNode + + // IsTermContext differentiates from other interfaces. + IsTermContext() +} + +type TermContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyTermContext() *TermContext { + var p = new(TermContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_term + return p +} + +func InitEmptyTermContext(p *TermContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_term +} + +func (*TermContext) IsTermContext() {} + +func NewTermContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TermContext { + var p = new(TermContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_term + + return p +} + +func (s *TermContext) GetParser() antlr.Parser { return s.parser } + +func (s *TermContext) Factor() IFactorContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IFactorContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IFactorContext) +} + +func (s *TermContext) Term() ITermContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ITermContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ITermContext) +} + +func (s *TermContext) STAR() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserSTAR, 0) +} + +func (s *TermContext) SLASH() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserSLASH, 0) +} + +func (s *TermContext) PERCENT() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserPERCENT, 0) +} + +func (s *TermContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *TermContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *TermContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterTerm(s) + } +} + +func (s *TermContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitTerm(s) + } +} + +func (s *TermContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitTerm(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) Term() (localctx ITermContext) { + return p.term(0) +} + +func (p *HavingExpressionParser) term(_p int) (localctx ITermContext) { + var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext() + + _parentState := p.GetState() + localctx = NewTermContext(p, p.GetParserRuleContext(), _parentState) + var _prevctx ITermContext = localctx + var _ antlr.ParserRuleContext = _prevctx // TODO: To prevent unused variable warning. + _startState := 20 + p.EnterRecursionRule(localctx, 20, HavingExpressionParserRULE_term, _p) + var _la int + + var _alt int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(122) + p.Factor() + } + + p.GetParserRuleContext().SetStop(p.GetTokenStream().LT(-1)) + p.SetState(129) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 12, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + if _alt == 1 { + if p.GetParseListeners() != nil { + p.TriggerExitRuleEvent() + } + _prevctx = localctx + localctx = NewTermContext(p, _parentctx, _parentState) + p.PushNewRecursionContext(localctx, _startState, HavingExpressionParserRULE_term) + p.SetState(124) + + if !(p.Precpred(p.GetParserRuleContext(), 2)) { + p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 2)", "")) + goto errorExit + } + { + p.SetState(125) + _la = p.GetTokenStream().LA(1) + + if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&229376) != 0) { + p.GetErrorHandler().RecoverInline(p) + } else { + p.GetErrorHandler().ReportMatch(p) + p.Consume() + } + } + { + p.SetState(126) + p.Factor() + } + + } + p.SetState(131) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 12, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.UnrollRecursionContexts(_parentctx) + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IFactorContext is an interface to support dynamic dispatch. +type IFactorContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + Factor() IFactorContext + PLUS() antlr.TerminalNode + MINUS() antlr.TerminalNode + LPAREN() antlr.TerminalNode + Operand() IOperandContext + RPAREN() antlr.TerminalNode + Atom() IAtomContext + + // IsFactorContext differentiates from other interfaces. + IsFactorContext() +} + +type FactorContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyFactorContext() *FactorContext { + var p = new(FactorContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_factor + return p +} + +func InitEmptyFactorContext(p *FactorContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_factor +} + +func (*FactorContext) IsFactorContext() {} + +func NewFactorContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FactorContext { + var p = new(FactorContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_factor + + return p +} + +func (s *FactorContext) GetParser() antlr.Parser { return s.parser } + +func (s *FactorContext) Factor() IFactorContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IFactorContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IFactorContext) +} + +func (s *FactorContext) PLUS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserPLUS, 0) +} + +func (s *FactorContext) MINUS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserMINUS, 0) +} + +func (s *FactorContext) LPAREN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserLPAREN, 0) +} + +func (s *FactorContext) Operand() IOperandContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IOperandContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IOperandContext) +} + +func (s *FactorContext) RPAREN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserRPAREN, 0) +} + +func (s *FactorContext) Atom() IAtomContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IAtomContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IAtomContext) +} + +func (s *FactorContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *FactorContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *FactorContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterFactor(s) + } +} + +func (s *FactorContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitFactor(s) + } +} + +func (s *FactorContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitFactor(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) Factor() (localctx IFactorContext) { + localctx = NewFactorContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 22, HavingExpressionParserRULE_factor) + var _la int + + p.SetState(139) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + + switch p.GetTokenStream().LA(1) { + case HavingExpressionParserPLUS, HavingExpressionParserMINUS: + p.EnterOuterAlt(localctx, 1) + { + p.SetState(132) + _la = p.GetTokenStream().LA(1) + + if !(_la == HavingExpressionParserPLUS || _la == HavingExpressionParserMINUS) { + p.GetErrorHandler().RecoverInline(p) + } else { + p.GetErrorHandler().ReportMatch(p) + p.Consume() + } + } + { + p.SetState(133) + p.Factor() + } + + case HavingExpressionParserLPAREN: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(134) + p.Match(HavingExpressionParserLPAREN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(135) + p.operand(0) + } + { + p.SetState(136) + p.Match(HavingExpressionParserRPAREN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserNUMBER, HavingExpressionParserIDENTIFIER, HavingExpressionParserSTRING: + p.EnterOuterAlt(localctx, 3) + { + p.SetState(138) + p.Atom() + } + + default: + p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + goto errorExit + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IAtomContext is an interface to support dynamic dispatch. +type IAtomContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + FunctionCall() IFunctionCallContext + Identifier() IIdentifierContext + NUMBER() antlr.TerminalNode + STRING() antlr.TerminalNode + + // IsAtomContext differentiates from other interfaces. + IsAtomContext() +} + +type AtomContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyAtomContext() *AtomContext { + var p = new(AtomContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_atom + return p +} + +func InitEmptyAtomContext(p *AtomContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_atom +} + +func (*AtomContext) IsAtomContext() {} + +func NewAtomContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AtomContext { + var p = new(AtomContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_atom + + return p +} + +func (s *AtomContext) GetParser() antlr.Parser { return s.parser } + +func (s *AtomContext) FunctionCall() IFunctionCallContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IFunctionCallContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IFunctionCallContext) +} + +func (s *AtomContext) Identifier() IIdentifierContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IIdentifierContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IIdentifierContext) +} + +func (s *AtomContext) NUMBER() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserNUMBER, 0) +} + +func (s *AtomContext) STRING() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserSTRING, 0) +} + +func (s *AtomContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *AtomContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *AtomContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterAtom(s) + } +} + +func (s *AtomContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitAtom(s) + } +} + +func (s *AtomContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitAtom(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) Atom() (localctx IAtomContext) { + localctx = NewAtomContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 24, HavingExpressionParserRULE_atom) + p.SetState(145) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + + switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 14, p.GetParserRuleContext()) { + case 1: + p.EnterOuterAlt(localctx, 1) + { + p.SetState(141) + p.FunctionCall() + } + + case 2: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(142) + p.Identifier() + } + + case 3: + p.EnterOuterAlt(localctx, 3) + { + p.SetState(143) + p.Match(HavingExpressionParserNUMBER) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case 4: + p.EnterOuterAlt(localctx, 4) + { + p.SetState(144) + p.Match(HavingExpressionParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case antlr.ATNInvalidAltNumber: + goto errorExit + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IFunctionCallContext is an interface to support dynamic dispatch. +type IFunctionCallContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + IDENTIFIER() antlr.TerminalNode + LPAREN() antlr.TerminalNode + RPAREN() antlr.TerminalNode + FunctionArgList() IFunctionArgListContext + + // IsFunctionCallContext differentiates from other interfaces. + IsFunctionCallContext() +} + +type FunctionCallContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyFunctionCallContext() *FunctionCallContext { + var p = new(FunctionCallContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_functionCall + return p +} + +func InitEmptyFunctionCallContext(p *FunctionCallContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_functionCall +} + +func (*FunctionCallContext) IsFunctionCallContext() {} + +func NewFunctionCallContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FunctionCallContext { + var p = new(FunctionCallContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_functionCall + + return p +} + +func (s *FunctionCallContext) GetParser() antlr.Parser { return s.parser } + +func (s *FunctionCallContext) IDENTIFIER() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserIDENTIFIER, 0) +} + +func (s *FunctionCallContext) LPAREN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserLPAREN, 0) +} + +func (s *FunctionCallContext) RPAREN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserRPAREN, 0) +} + +func (s *FunctionCallContext) FunctionArgList() IFunctionArgListContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IFunctionArgListContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IFunctionArgListContext) +} + +func (s *FunctionCallContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *FunctionCallContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *FunctionCallContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterFunctionCall(s) + } +} + +func (s *FunctionCallContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitFunctionCall(s) + } +} + +func (s *FunctionCallContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitFunctionCall(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) FunctionCall() (localctx IFunctionCallContext) { + localctx = NewFunctionCallContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 26, HavingExpressionParserRULE_functionCall) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(147) + p.Match(HavingExpressionParserIDENTIFIER) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(148) + p.Match(HavingExpressionParserLPAREN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + p.SetState(150) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&65011650) != 0 { + { + p.SetState(149) + p.FunctionArgList() + } + + } + { + p.SetState(152) + p.Match(HavingExpressionParserRPAREN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IFunctionArgListContext is an interface to support dynamic dispatch. +type IFunctionArgListContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllFuncArg() []IFuncArgContext + FuncArg(i int) IFuncArgContext + AllCOMMA() []antlr.TerminalNode + COMMA(i int) antlr.TerminalNode + + // IsFunctionArgListContext differentiates from other interfaces. + IsFunctionArgListContext() +} + +type FunctionArgListContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyFunctionArgListContext() *FunctionArgListContext { + var p = new(FunctionArgListContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_functionArgList + return p +} + +func InitEmptyFunctionArgListContext(p *FunctionArgListContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_functionArgList +} + +func (*FunctionArgListContext) IsFunctionArgListContext() {} + +func NewFunctionArgListContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FunctionArgListContext { + var p = new(FunctionArgListContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_functionArgList + + return p +} + +func (s *FunctionArgListContext) GetParser() antlr.Parser { return s.parser } + +func (s *FunctionArgListContext) AllFuncArg() []IFuncArgContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IFuncArgContext); ok { + len++ + } + } + + tst := make([]IFuncArgContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IFuncArgContext); ok { + tst[i] = t.(IFuncArgContext) + i++ + } + } + + return tst +} + +func (s *FunctionArgListContext) FuncArg(i int) IFuncArgContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IFuncArgContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IFuncArgContext) +} + +func (s *FunctionArgListContext) AllCOMMA() []antlr.TerminalNode { + return s.GetTokens(HavingExpressionParserCOMMA) +} + +func (s *FunctionArgListContext) COMMA(i int) antlr.TerminalNode { + return s.GetToken(HavingExpressionParserCOMMA, i) +} + +func (s *FunctionArgListContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *FunctionArgListContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *FunctionArgListContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterFunctionArgList(s) + } +} + +func (s *FunctionArgListContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitFunctionArgList(s) + } +} + +func (s *FunctionArgListContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitFunctionArgList(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) FunctionArgList() (localctx IFunctionArgListContext) { + localctx = NewFunctionArgListContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 28, HavingExpressionParserRULE_functionArgList) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(154) + p.FuncArg() + } + p.SetState(159) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + for _la == HavingExpressionParserCOMMA { + { + p.SetState(155) + p.Match(HavingExpressionParserCOMMA) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(156) + p.FuncArg() + } + + p.SetState(161) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IFuncArgContext is an interface to support dynamic dispatch. +type IFuncArgContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllFuncArgToken() []IFuncArgTokenContext + FuncArgToken(i int) IFuncArgTokenContext + + // IsFuncArgContext differentiates from other interfaces. + IsFuncArgContext() +} + +type FuncArgContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyFuncArgContext() *FuncArgContext { + var p = new(FuncArgContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_funcArg + return p +} + +func InitEmptyFuncArgContext(p *FuncArgContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_funcArg +} + +func (*FuncArgContext) IsFuncArgContext() {} + +func NewFuncArgContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FuncArgContext { + var p = new(FuncArgContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_funcArg + + return p +} + +func (s *FuncArgContext) GetParser() antlr.Parser { return s.parser } + +func (s *FuncArgContext) AllFuncArgToken() []IFuncArgTokenContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IFuncArgTokenContext); ok { + len++ + } + } + + tst := make([]IFuncArgTokenContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IFuncArgTokenContext); ok { + tst[i] = t.(IFuncArgTokenContext) + i++ + } + } + + return tst +} + +func (s *FuncArgContext) FuncArgToken(i int) IFuncArgTokenContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IFuncArgTokenContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IFuncArgTokenContext) +} + +func (s *FuncArgContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *FuncArgContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *FuncArgContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterFuncArg(s) + } +} + +func (s *FuncArgContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitFuncArg(s) + } +} + +func (s *FuncArgContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitFuncArg(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) FuncArg() (localctx IFuncArgContext) { + localctx = NewFuncArgContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 30, HavingExpressionParserRULE_funcArg) + var _la int + + p.EnterOuterAlt(localctx, 1) + p.SetState(163) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + for ok := true; ok; ok = ((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&65011650) != 0) { + { + p.SetState(162) + p.FuncArgToken() + } + + p.SetState(165) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IFuncArgTokenContext is an interface to support dynamic dispatch. +type IFuncArgTokenContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + IDENTIFIER() antlr.TerminalNode + STRING() antlr.TerminalNode + NUMBER() antlr.TerminalNode + BOOL() antlr.TerminalNode + EQUALS() antlr.TerminalNode + NOT_EQUALS() antlr.TerminalNode + NEQ() antlr.TerminalNode + LT() antlr.TerminalNode + LE() antlr.TerminalNode + GT() antlr.TerminalNode + GE() antlr.TerminalNode + PLUS() antlr.TerminalNode + MINUS() antlr.TerminalNode + STAR() antlr.TerminalNode + SLASH() antlr.TerminalNode + PERCENT() antlr.TerminalNode + NOT() antlr.TerminalNode + AND() antlr.TerminalNode + OR() antlr.TerminalNode + LPAREN() antlr.TerminalNode + RPAREN() antlr.TerminalNode + AllFuncArgToken() []IFuncArgTokenContext + FuncArgToken(i int) IFuncArgTokenContext + + // IsFuncArgTokenContext differentiates from other interfaces. + IsFuncArgTokenContext() +} + +type FuncArgTokenContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyFuncArgTokenContext() *FuncArgTokenContext { + var p = new(FuncArgTokenContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_funcArgToken + return p +} + +func InitEmptyFuncArgTokenContext(p *FuncArgTokenContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_funcArgToken +} + +func (*FuncArgTokenContext) IsFuncArgTokenContext() {} + +func NewFuncArgTokenContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FuncArgTokenContext { + var p = new(FuncArgTokenContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_funcArgToken + + return p +} + +func (s *FuncArgTokenContext) GetParser() antlr.Parser { return s.parser } + +func (s *FuncArgTokenContext) IDENTIFIER() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserIDENTIFIER, 0) +} + +func (s *FuncArgTokenContext) STRING() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserSTRING, 0) +} + +func (s *FuncArgTokenContext) NUMBER() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserNUMBER, 0) +} + +func (s *FuncArgTokenContext) BOOL() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserBOOL, 0) +} + +func (s *FuncArgTokenContext) EQUALS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserEQUALS, 0) +} + +func (s *FuncArgTokenContext) NOT_EQUALS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserNOT_EQUALS, 0) +} + +func (s *FuncArgTokenContext) NEQ() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserNEQ, 0) +} + +func (s *FuncArgTokenContext) LT() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserLT, 0) +} + +func (s *FuncArgTokenContext) LE() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserLE, 0) +} + +func (s *FuncArgTokenContext) GT() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserGT, 0) +} + +func (s *FuncArgTokenContext) GE() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserGE, 0) +} + +func (s *FuncArgTokenContext) PLUS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserPLUS, 0) +} + +func (s *FuncArgTokenContext) MINUS() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserMINUS, 0) +} + +func (s *FuncArgTokenContext) STAR() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserSTAR, 0) +} + +func (s *FuncArgTokenContext) SLASH() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserSLASH, 0) +} + +func (s *FuncArgTokenContext) PERCENT() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserPERCENT, 0) +} + +func (s *FuncArgTokenContext) NOT() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserNOT, 0) +} + +func (s *FuncArgTokenContext) AND() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserAND, 0) +} + +func (s *FuncArgTokenContext) OR() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserOR, 0) +} + +func (s *FuncArgTokenContext) LPAREN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserLPAREN, 0) +} + +func (s *FuncArgTokenContext) RPAREN() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserRPAREN, 0) +} + +func (s *FuncArgTokenContext) AllFuncArgToken() []IFuncArgTokenContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IFuncArgTokenContext); ok { + len++ + } + } + + tst := make([]IFuncArgTokenContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IFuncArgTokenContext); ok { + tst[i] = t.(IFuncArgTokenContext) + i++ + } + } + + return tst +} + +func (s *FuncArgTokenContext) FuncArgToken(i int) IFuncArgTokenContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IFuncArgTokenContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IFuncArgTokenContext) +} + +func (s *FuncArgTokenContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *FuncArgTokenContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *FuncArgTokenContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterFuncArgToken(s) + } +} + +func (s *FuncArgTokenContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitFuncArgToken(s) + } +} + +func (s *FuncArgTokenContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitFuncArgToken(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) FuncArgToken() (localctx IFuncArgTokenContext) { + localctx = NewFuncArgTokenContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 32, HavingExpressionParserRULE_funcArgToken) + var _la int + + p.SetState(194) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + + switch p.GetTokenStream().LA(1) { + case HavingExpressionParserIDENTIFIER: + p.EnterOuterAlt(localctx, 1) + { + p.SetState(167) + p.Match(HavingExpressionParserIDENTIFIER) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserSTRING: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(168) + p.Match(HavingExpressionParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserNUMBER: + p.EnterOuterAlt(localctx, 3) + { + p.SetState(169) + p.Match(HavingExpressionParserNUMBER) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserBOOL: + p.EnterOuterAlt(localctx, 4) + { + p.SetState(170) + p.Match(HavingExpressionParserBOOL) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserEQUALS: + p.EnterOuterAlt(localctx, 5) + { + p.SetState(171) + p.Match(HavingExpressionParserEQUALS) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserNOT_EQUALS: + p.EnterOuterAlt(localctx, 6) + { + p.SetState(172) + p.Match(HavingExpressionParserNOT_EQUALS) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserNEQ: + p.EnterOuterAlt(localctx, 7) + { + p.SetState(173) + p.Match(HavingExpressionParserNEQ) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserLT: + p.EnterOuterAlt(localctx, 8) + { + p.SetState(174) + p.Match(HavingExpressionParserLT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserLE: + p.EnterOuterAlt(localctx, 9) + { + p.SetState(175) + p.Match(HavingExpressionParserLE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserGT: + p.EnterOuterAlt(localctx, 10) + { + p.SetState(176) + p.Match(HavingExpressionParserGT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserGE: + p.EnterOuterAlt(localctx, 11) + { + p.SetState(177) + p.Match(HavingExpressionParserGE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserPLUS: + p.EnterOuterAlt(localctx, 12) + { + p.SetState(178) + p.Match(HavingExpressionParserPLUS) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserMINUS: + p.EnterOuterAlt(localctx, 13) + { + p.SetState(179) + p.Match(HavingExpressionParserMINUS) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserSTAR: + p.EnterOuterAlt(localctx, 14) + { + p.SetState(180) + p.Match(HavingExpressionParserSTAR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserSLASH: + p.EnterOuterAlt(localctx, 15) + { + p.SetState(181) + p.Match(HavingExpressionParserSLASH) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserPERCENT: + p.EnterOuterAlt(localctx, 16) + { + p.SetState(182) + p.Match(HavingExpressionParserPERCENT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserNOT: + p.EnterOuterAlt(localctx, 17) + { + p.SetState(183) + p.Match(HavingExpressionParserNOT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserAND: + p.EnterOuterAlt(localctx, 18) + { + p.SetState(184) + p.Match(HavingExpressionParserAND) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserOR: + p.EnterOuterAlt(localctx, 19) + { + p.SetState(185) + p.Match(HavingExpressionParserOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case HavingExpressionParserLPAREN: + p.EnterOuterAlt(localctx, 20) + { + p.SetState(186) + p.Match(HavingExpressionParserLPAREN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + p.SetState(190) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + for (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&65011650) != 0 { + { + p.SetState(187) + p.FuncArgToken() + } + + p.SetState(192) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + } + { + p.SetState(193) + p.Match(HavingExpressionParserRPAREN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + default: + p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + goto errorExit + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IIdentifierContext is an interface to support dynamic dispatch. +type IIdentifierContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + IDENTIFIER() antlr.TerminalNode + + // IsIdentifierContext differentiates from other interfaces. + IsIdentifierContext() +} + +type IdentifierContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIdentifierContext() *IdentifierContext { + var p = new(IdentifierContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_identifier + return p +} + +func InitEmptyIdentifierContext(p *IdentifierContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HavingExpressionParserRULE_identifier +} + +func (*IdentifierContext) IsIdentifierContext() {} + +func NewIdentifierContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *IdentifierContext { + var p = new(IdentifierContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HavingExpressionParserRULE_identifier + + return p +} + +func (s *IdentifierContext) GetParser() antlr.Parser { return s.parser } + +func (s *IdentifierContext) IDENTIFIER() antlr.TerminalNode { + return s.GetToken(HavingExpressionParserIDENTIFIER, 0) +} + +func (s *IdentifierContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IdentifierContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *IdentifierContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.EnterIdentifier(s) + } +} + +func (s *IdentifierContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HavingExpressionListener); ok { + listenerT.ExitIdentifier(s) + } +} + +func (s *IdentifierContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case HavingExpressionVisitor: + return t.VisitIdentifier(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *HavingExpressionParser) Identifier() (localctx IIdentifierContext) { + localctx = NewIdentifierContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 34, HavingExpressionParserRULE_identifier) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(196) + p.Match(HavingExpressionParserIDENTIFIER) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +func (p *HavingExpressionParser) Sempred(localctx antlr.RuleContext, ruleIndex, predIndex int) bool { + switch ruleIndex { + case 9: + var t *OperandContext = nil + if localctx != nil { + t = localctx.(*OperandContext) + } + return p.Operand_Sempred(t, predIndex) + + case 10: + var t *TermContext = nil + if localctx != nil { + t = localctx.(*TermContext) + } + return p.Term_Sempred(t, predIndex) + + default: + panic("No predicate with index: " + fmt.Sprint(ruleIndex)) + } +} + +func (p *HavingExpressionParser) Operand_Sempred(localctx antlr.RuleContext, predIndex int) bool { + switch predIndex { + case 0: + return p.Precpred(p.GetParserRuleContext(), 2) + + default: + panic("No predicate with index: " + fmt.Sprint(predIndex)) + } +} + +func (p *HavingExpressionParser) Term_Sempred(localctx antlr.RuleContext, predIndex int) bool { + switch predIndex { + case 1: + return p.Precpred(p.GetParserRuleContext(), 2) + + default: + panic("No predicate with index: " + fmt.Sprint(predIndex)) + } +} diff --git a/pkg/parser/havingexpression/grammar/havingexpression_visitor.go b/pkg/parser/havingexpression/grammar/havingexpression_visitor.go new file mode 100644 index 000000000..f7af3f8ba --- /dev/null +++ b/pkg/parser/havingexpression/grammar/havingexpression_visitor.go @@ -0,0 +1,64 @@ +// Code generated from grammar/HavingExpression.g4 by ANTLR 4.13.2. DO NOT EDIT. + +package parser // HavingExpression + +import "github.com/antlr4-go/antlr/v4" + +// A complete Visitor for a parse tree produced by HavingExpressionParser. +type HavingExpressionVisitor interface { + antlr.ParseTreeVisitor + + // Visit a parse tree produced by HavingExpressionParser#query. + VisitQuery(ctx *QueryContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#expression. + VisitExpression(ctx *ExpressionContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#orExpression. + VisitOrExpression(ctx *OrExpressionContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#andExpression. + VisitAndExpression(ctx *AndExpressionContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#primary. + VisitPrimary(ctx *PrimaryContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#comparison. + VisitComparison(ctx *ComparisonContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#compOp. + VisitCompOp(ctx *CompOpContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#inList. + VisitInList(ctx *InListContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#signedNumber. + VisitSignedNumber(ctx *SignedNumberContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#operand. + VisitOperand(ctx *OperandContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#term. + VisitTerm(ctx *TermContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#factor. + VisitFactor(ctx *FactorContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#atom. + VisitAtom(ctx *AtomContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#functionCall. + VisitFunctionCall(ctx *FunctionCallContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#functionArgList. + VisitFunctionArgList(ctx *FunctionArgListContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#funcArg. + VisitFuncArg(ctx *FuncArgContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#funcArgToken. + VisitFuncArgToken(ctx *FuncArgTokenContext) interface{} + + // Visit a parse tree produced by HavingExpressionParser#identifier. + VisitIdentifier(ctx *IdentifierContext) interface{} +} diff --git a/pkg/querybuilder/filter_error_listener.go b/pkg/querybuilder/filter_error_listener.go index b4dc9a3e6..5affddae4 100644 --- a/pkg/querybuilder/filter_error_listener.go +++ b/pkg/querybuilder/filter_error_listener.go @@ -36,6 +36,7 @@ var friendly = map[string]string{ // literals / identifiers "NUMBER": "number", + "STRING": "string", "BOOL": "boolean", "QUOTED_TEXT": "quoted text", "KEY": "field name (ex: service.name)", @@ -88,6 +89,8 @@ func (e *SyntaxErr) Error() string { exp := "" if len(e.Expected) > 0 { exp = "expecting one of {" + strings.Join(e.Expected, ", ") + "}" + " but got " + e.TokenTxt + } else if e.Msg != "" { + exp = e.Msg } return fmt.Sprintf("line %d:%d %s", e.Line, e.Col, exp) } @@ -154,9 +157,48 @@ func (l *ErrorListener) SyntaxError( } } + // Check which "closing" tokens are in the expected set so we can suppress + // context-inappropriate tokens from the displayed set: + // - When RPAREN is expected, hide LBRACK/RBRACK (IN-list brackets confuse + // unclosed-paren errors, e.g. "{), [}" → "{)}") + // - When RBRACK is expected, hide COMMA (inside an IN bracket list the only + // meaningful fix is to close the bracket, e.g. "{,, ]}" → "{]}") + rparenType := pGetTokenType(p, "RPAREN") + lbrackType := pGetTokenType(p, "LBRACK") + rbrackType := pGetTokenType(p, "RBRACK") + commaType := pGetTokenType(p, "COMMA") + hasRParen, hasRBrack := false, false + for _, iv := range set.GetIntervals() { + for t := iv.Start; t <= iv.Stop; t++ { + if t == rparenType { + hasRParen = true + } + if t == rbrackType { + hasRBrack = true + } + } + } + uniq := map[string]struct{}{} for _, iv := range set.GetIntervals() { for t := iv.Start; t <= iv.Stop; t++ { + // Exclude the offending token itself from the expected set. + // ANTLR's error recovery sometimes leaves the offending token in + // the follow-set, producing a contradictory message like + // "expecting NOT but got 'NOT'". + if t == err.TokenType { + continue + } + // When RPAREN is expected, suppress LBRACK/RBRACK — they only + // appear in IN-list contexts and confuse unclosed-paren errors. + if hasRParen && (t == lbrackType || t == rbrackType) { + continue + } + // When RBRACK is expected (inside an IN bracket list), suppress + // COMMA — the only useful fix is closing the bracket. + if hasRBrack && t == commaType { + continue + } sym := tokenName(p, t) if sym == "KEY" { if !hasValueLiterals { diff --git a/pkg/querybuilder/having_expression_validator.go b/pkg/querybuilder/having_expression_validator.go new file mode 100644 index 000000000..76c38c0b3 --- /dev/null +++ b/pkg/querybuilder/having_expression_validator.go @@ -0,0 +1,496 @@ +package querybuilder + +import ( + "sort" + "strings" + + "github.com/SigNoz/signoz/pkg/errors" + grammar "github.com/SigNoz/signoz/pkg/parser/havingexpression/grammar" + "github.com/antlr4-go/antlr/v4" + "github.com/huandu/go-sqlbuilder" +) + +// havingExpressionRewriteVisitor walks the parse tree of a HavingExpression in a single +// pass, simultaneously rewriting user-facing references to their SQL column names and +// collecting any references that could not be resolved. +// +// Each visit method reconstructs the expression string for its subtree: +// - Structural nodes (orExpression, andExpression, comparison, arithmetic) are +// reconstructed with canonical spacing. +// - andExpression joins ALL primaries with " AND ", which naturally normalises any +// implicit-AND adjacency (the old normalizeImplicitAND step). +// - IdentifierContext looks the name up in columnMap; if found the SQL column name is +// returned. If the name is already a valid SQL column (TO side of columnMap) it is +// passed through unchanged. Otherwise it is added to invalid. +// - FunctionCallContext looks the full call text (without whitespace, since WS is +// skipped) up in columnMap; if found the SQL column name is returned, otherwise the +// function name is added to invalid without recursing into its arguments. +// The grammar now accepts complex function arguments (nested calls, string predicates), +// so all aggregation expression forms can be looked up directly via ctx.GetText(). +// - STRING atoms (string literals in comparison position) set hasStringLiteral so a +// friendly "aggregator results are numeric" error can be returned. +type havingExpressionRewriteVisitor struct { + columnMap map[string]string + validColumns map[string]bool // TO-side values; identifiers already in SQL form pass through + invalid []string + seen map[string]bool + hasStringLiteral bool + sb *sqlbuilder.SelectBuilder +} + +func newHavingExpressionRewriteVisitor(columnMap map[string]string) *havingExpressionRewriteVisitor { + validColumns := make(map[string]bool, len(columnMap)) + for _, col := range columnMap { + validColumns[col] = true + } + return &havingExpressionRewriteVisitor{ + columnMap: columnMap, + validColumns: validColumns, + seen: make(map[string]bool), + sb: sqlbuilder.NewSelectBuilder(), + } +} + +func (v *havingExpressionRewriteVisitor) visitQuery(ctx grammar.IQueryContext) string { + if ctx.Expression() == nil { + return "" + } + return v.visitExpression(ctx.Expression()) +} + +func (v *havingExpressionRewriteVisitor) visitExpression(ctx grammar.IExpressionContext) string { + return v.visitOrExpression(ctx.OrExpression()) +} + +func (v *havingExpressionRewriteVisitor) visitOrExpression(ctx grammar.IOrExpressionContext) string { + andExprs := ctx.AllAndExpression() + parts := make([]string, len(andExprs)) + for i, ae := range andExprs { + parts[i] = v.visitAndExpression(ae) + } + if len(parts) == 1 { + return parts[0] + } + return v.sb.Or(parts...) +} + +// visitAndExpression joins ALL primaries with " AND ". +// The grammar rule `primary ( AND primary | primary )*` allows adjacent primaries +// without an explicit AND (implicit AND). Joining all of them with " AND " here is +// equivalent to the old normalizeImplicitAND step. +func (v *havingExpressionRewriteVisitor) visitAndExpression(ctx grammar.IAndExpressionContext) string { + primaries := ctx.AllPrimary() + parts := make([]string, len(primaries)) + for i, p := range primaries { + parts[i] = v.visitPrimary(p) + } + if len(parts) == 1 { + return parts[0] + } + return v.sb.And(parts...) +} + +func (v *havingExpressionRewriteVisitor) visitPrimary(ctx grammar.IPrimaryContext) string { + if ctx.OrExpression() != nil { + inner := v.visitOrExpression(ctx.OrExpression()) + if ctx.NOT() != nil { + return v.sb.Not(inner) + } + return v.sb.And(inner) + } + if ctx.Comparison() == nil { + return "" + } + inner := v.visitComparison(ctx.Comparison()) + if ctx.NOT() != nil { + return v.sb.Not(inner) + } + return inner +} + +func (v *havingExpressionRewriteVisitor) visitComparison(ctx grammar.IComparisonContext) string { + if ctx.IN() != nil { + if ctx.Operand(0) == nil || ctx.InList() == nil { + return "" + } + lhs := v.visitOperand(ctx.Operand(0)) + signedNumbers := ctx.InList().AllSignedNumber() + vals := make([]interface{}, len(signedNumbers)) + for i, n := range signedNumbers { + vals[i] = sqlbuilder.Raw(n.GetText()) + } + if ctx.NOT() != nil { + // Here we need to compile because In generates lhs IN $1 syntax + sql, _ := v.sb.Args.CompileWithFlavor(v.sb.NotIn(lhs, vals...), sqlbuilder.ClickHouse) + return sql + } + // Here we need to compile because In generates lhs IN $1 syntax + sql, _ := v.sb.Args.CompileWithFlavor(v.sb.In(lhs, vals...), sqlbuilder.ClickHouse) + return sql + } + if ctx.CompOp() == nil || ctx.Operand(0) == nil || ctx.Operand(1) == nil { + return "" + } + lhs := v.visitOperand(ctx.Operand(0)) + op := ctx.CompOp().GetText() + rhs := v.visitOperand(ctx.Operand(1)) + return lhs + " " + op + " " + rhs +} + +func (v *havingExpressionRewriteVisitor) visitOperand(ctx grammar.IOperandContext) string { + if ctx.Operand() != nil { + left := v.visitOperand(ctx.Operand()) + right := v.visitTerm(ctx.Term()) + op := "+" + if ctx.MINUS() != nil { + op = "-" + } + return left + " " + op + " " + right + } + return v.visitTerm(ctx.Term()) +} + +func (v *havingExpressionRewriteVisitor) visitTerm(ctx grammar.ITermContext) string { + if ctx.Term() != nil { + left := v.visitTerm(ctx.Term()) + right := v.visitFactor(ctx.Factor()) + op := "*" + if ctx.SLASH() != nil { + op = "/" + } else if ctx.PERCENT() != nil { + op = "%" + } + return left + " " + op + " " + right + } + return v.visitFactor(ctx.Factor()) +} + +func (v *havingExpressionRewriteVisitor) visitFactor(ctx grammar.IFactorContext) string { + if ctx.Factor() != nil { + // Unary sign: (PLUS | MINUS) factor + sign := "+" + if ctx.MINUS() != nil { + sign = "-" + } + return sign + v.visitFactor(ctx.Factor()) + } + if ctx.Operand() != nil { + return v.sb.And(v.visitOperand(ctx.Operand())) + } + if ctx.Atom() == nil { + return "" + } + return v.visitAtom(ctx.Atom()) +} + +func (v *havingExpressionRewriteVisitor) visitAtom(ctx grammar.IAtomContext) string { + if ctx.FunctionCall() != nil { + return v.visitFunctionCall(ctx.FunctionCall()) + } + if ctx.Identifier() != nil { + return v.visitIdentifier(ctx.Identifier()) + } + if ctx.STRING() != nil { + // String literals are never valid aggregation results; flag for a friendly error. + v.hasStringLiteral = true + return ctx.STRING().GetText() + } + text := ctx.NUMBER().GetText() + return text +} + +// visitFunctionCall looks the full call text up in columnMap. WS tokens are skipped by +// the lexer, so ctx.GetText() returns the expression with all whitespace removed +// (e.g. "countIf(level='error')", "avg(sum(cpu_usage))", "count_distinct(a,b)"). +// The column map stores both the original expression and a space-stripped version as +// keys, so the lookup is whitespace-insensitive regardless of how the user typed it. +// If not found, the function name is recorded as invalid. +func (v *havingExpressionRewriteVisitor) visitFunctionCall(ctx grammar.IFunctionCallContext) string { + fullText := ctx.GetText() + if col, ok := v.columnMap[fullText]; ok { + return col + } + funcName := ctx.IDENTIFIER().GetText() + if !v.seen[funcName] { + v.invalid = append(v.invalid, funcName) + v.seen[funcName] = true + } + return fullText +} + +// visitIdentifier looks the identifier up in columnMap. If found, returns the SQL +// column name. If the name is already a valid SQL column (validColumns), it is passed +// through unchanged — this handles cases where the user writes the SQL column name +// directly (e.g. __result_0). Otherwise records it as invalid. +func (v *havingExpressionRewriteVisitor) visitIdentifier(ctx grammar.IIdentifierContext) string { + name := ctx.IDENTIFIER().GetText() + if col, ok := v.columnMap[name]; ok { + return col + } + if v.validColumns[name] { + return name + } + if !v.seen[name] { + v.invalid = append(v.invalid, name) + v.seen[name] = true + } + return name +} + +// rewriteAndValidate is the single-pass implementation used by all RewriteFor* methods. +// +// Validation layers: +// 1. The visitor runs on the parse tree, rewriting and collecting invalid references. +// Unknown references (including unrecognised function calls) → lists valid references. +// The grammar now supports complex function arguments (nested calls, string predicates) +// so all aggregation expression forms are handled directly by the parser without any +// regex pre-substitution. +// 2. String literals in comparison-operand position → descriptive error +// ("aggregator results are numeric"). +// 3. ANTLR syntax errors → error with messages referencing the original token names. +func (r *HavingExpressionRewriter) rewriteAndValidate(expression string) (string, error) { + original := strings.TrimSpace(expression) + + // Parse the expression once. + input := antlr.NewInputStream(expression) + lexer := grammar.NewHavingExpressionLexer(input) + + lexerErrListener := NewErrorListener() + lexer.RemoveErrorListeners() + lexer.AddErrorListener(lexerErrListener) + + tokens := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) + p := grammar.NewHavingExpressionParser(tokens) + + parserErrListener := NewErrorListener() + p.RemoveErrorListeners() + p.AddErrorListener(parserErrListener) + + tree := p.Query() + + // Layer 1 – run the combined visitor and report any unresolved references. + // This runs before the syntax error check so that expressions with recoverable + // parse errors (e.g. sum(count())) still produce an actionable "invalid reference" + // message rather than a raw syntax error. + v := newHavingExpressionRewriteVisitor(r.columnMap) + result := v.visitQuery(tree) + + // Layer 2 – string literals in comparison-operand position (atom rule). + // The grammar accepts STRING tokens in atom so the parser can recover and continue, + // but the visitor flags them; aggregator results are always numeric. + // This is checked before invalid references so that "contains string literals" takes + // priority when a bare string literal is also an unresolvable operand. + if v.hasStringLiteral { + return "", errors.NewInvalidInputf( + errors.CodeInvalidInput, + "`Having` expression contains string literals", + ).WithAdditional("Aggregator results are numeric") + } + + if len(v.invalid) > 0 { + sort.Strings(v.invalid) + validKeys := make([]string, 0, len(r.columnMap)) + for k := range r.columnMap { + validKeys = append(validKeys, k) + } + sort.Strings(validKeys) + additional := []string{"Valid references are: [" + strings.Join(validKeys, ", ") + "]"} + if len(v.invalid) == 1 { + inv := v.invalid[0] + // Only suggest for plain identifier typos, not for unresolved function + // calls: a function call will appear as "name(" in the expression, and + // the closest valid key may itself contain "(" (e.g. "sum(a)"), making + // a simple string substitution produce a corrupt expression. + isFuncCall := strings.Contains(original, inv+"(") + if match, dist := closestMatch(inv, validKeys); !isFuncCall && !strings.Contains(match, "(") && dist <= 3 { + corrected := strings.ReplaceAll(original, inv, match) + additional = append(additional, "Suggestion: `"+corrected+"`") + } + } + return "", errors.NewInvalidInputf( + errors.CodeInvalidInput, + "Invalid references in `Having` expression: [%s]", + strings.Join(v.invalid, ", "), + ).WithAdditional(additional...) + } + + // Layer 3 – ANTLR syntax errors. We parse the original expression, so error messages + // already reference the user's own token names; no re-parsing is needed. + allSyntaxErrors := append(lexerErrListener.SyntaxErrors, parserErrListener.SyntaxErrors...) + if len(allSyntaxErrors) > 0 { + msgs := make([]string, 0, len(allSyntaxErrors)) + for _, se := range allSyntaxErrors { + if m := se.Error(); m != "" { + msgs = append(msgs, m) + } + } + detail := strings.Join(msgs, "; ") + if detail == "" { + detail = "check the expression syntax" + } + additional := []string{detail} + // For single-error expressions, try to produce an actionable suggestion. + if len(allSyntaxErrors) == 1 { + if s := havingSuggestion(allSyntaxErrors[0], original); s != "" { + additional = append(additional, "Suggestion: `"+s+"`") + } + } + return "", errors.NewInvalidInputf( + errors.CodeInvalidInput, + "Syntax error in `Having` expression", + ).WithAdditional(additional...) + } + + return result, nil +} + +// havingSuggestion returns a corrected expression string to show as a suggestion when +// the error matches a well-known single-mistake pattern, or "" when no suggestion +// can be formed. Only call this when there is exactly one syntax error. +// +// Recognised patterns (all produce a minimal, valid completion): +// 1. Bare aggregation — comparison operator expected at EOF: count() → count() > 0 +// 2. Missing right operand after comparison op at EOF: count() > → count() > 0 +// 3. Unclosed parenthesis — only ) expected at EOF: (total > 100 → (total > 100) +// 4. Dangling AND/OR at end of expression: total > 100 AND → total > 100 +// 5. Leading OR at position 0: OR total > 100 → total > 100 +func havingSuggestion(se *SyntaxErr, original string) string { + trimmed := strings.TrimSpace(original) + upper := strings.ToUpper(trimmed) + + if se.TokenTxt == "EOF" { + // Pattern 4: dangling AND or OR at end of expression. + // e.g. total > 100 AND → total > 100 + // Checked before Pattern 1 so that "expr AND" does not match Pattern 1. + if strings.HasSuffix(upper, " AND") { + return strings.TrimSpace(trimmed[:len(trimmed)-4]) + } + if strings.HasSuffix(upper, " OR") { + return strings.TrimSpace(trimmed[:len(trimmed)-3]) + } + + // Pattern 1: bare aggregation reference — no comparison operator yet. + // Detected by: IDENTIFIER in expected (operand-continuation set), expression + // does not already end with a comparison operator (Pattern 2 handles that case), + // and no unclosed parenthesis (Pattern 3 handles that case). + // e.g. count() → count() > 0 + // total_logs → total_logs > 0 + if expectedContains(se, "IDENTIFIER") && !endsWithComparisonOp(trimmed) && !hasUnclosedParen(trimmed) { + return trimmed + " > 0" + } + + // Pattern 2: comparison operator already written but right operand missing. + // e.g. count() > → count() > 0 + if expectedContains(se, "number") && endsWithComparisonOp(trimmed) { + return trimmed + " 0" + } + + // Pattern 3: unclosed parenthesis with content inside. + // e.g. (total > 100 AND count() < 500 → (total > 100 AND count() < 500) + // Guard len > 1 avoids a useless "()" suggestion for a bare "(". + if expectedContains(se, ")") && hasUnclosedParen(trimmed) && len(trimmed) > 1 { + return trimmed + ")" + } + + // Pattern 6: unclosed IN bracket list. + // e.g. count() IN [1, 2, 3 → count() IN [1, 2, 3] + if expectedContains(se, "]") && hasUnclosedBracket(trimmed) && len(trimmed) > 1 { + return trimmed + "]" + } + + return "" + } + + // Pattern 5: leading OR at position 0. + // e.g. OR total > 100 → total > 100 + if se.TokenTxt == "'OR'" && se.Col == 0 && strings.HasPrefix(upper, "OR ") { + return strings.TrimSpace(trimmed[3:]) + } + + return "" +} + +// expectedContains reports whether label is present in se.Expected. +func expectedContains(se *SyntaxErr, label string) bool { + for _, e := range se.Expected { + if e == label { + return true + } + } + return false +} + +// hasUnclosedParen reports whether s contains more '(' than ')'. +func hasUnclosedParen(s string) bool { + count := 0 + for _, c := range s { + switch c { + case '(': + count++ + case ')': + count-- + } + } + return count > 0 +} + +// hasUnclosedBracket reports whether s contains more '[' than ']'. +func hasUnclosedBracket(s string) bool { + count := 0 + for _, c := range s { + switch c { + case '[': + count++ + case ']': + count-- + } + } + return count > 0 +} + +// closestMatch returns the element of candidates with the smallest Levenshtein +// distance to query, along with that distance. +func closestMatch(query string, candidates []string) (string, int) { + best, bestDist := "", -1 + for _, c := range candidates { + if d := levenshtein(query, c); bestDist < 0 || d < bestDist { + best, bestDist = c, d + } + } + return best, bestDist +} + +// levenshtein computes the edit distance between a and b. +func levenshtein(a, b string) int { + ra, rb := []rune(a), []rune(b) + la, lb := len(ra), len(rb) + row := make([]int, lb+1) + for j := range row { + row[j] = j + } + for i := 1; i <= la; i++ { + prev := row[0] + row[0] = i + for j := 1; j <= lb; j++ { + tmp := row[j] + if ra[i-1] == rb[j-1] { + row[j] = prev + } else { + row[j] = 1 + min(prev, min(row[j], row[j-1])) + } + prev = tmp + } + } + return row[lb] +} + +// endsWithComparisonOp reports whether s ends with a comparison operator token +// (longer operators are checked first to avoid ">=" being matched by ">"). +func endsWithComparisonOp(s string) bool { + for _, op := range []string{">=", "<=", "!=", "<>", "==", ">", "<", "="} { + if strings.HasSuffix(s, op) { + return true + } + } + return false +} diff --git a/pkg/querybuilder/having_rewriter.go b/pkg/querybuilder/having_rewriter.go index 6e75ba32d..2b8701ca6 100644 --- a/pkg/querybuilder/having_rewriter.go +++ b/pkg/querybuilder/having_rewriter.go @@ -2,7 +2,6 @@ package querybuilder import ( "fmt" - "regexp" "strings" qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" @@ -19,19 +18,31 @@ func NewHavingExpressionRewriter() *HavingExpressionRewriter { } } -func (r *HavingExpressionRewriter) RewriteForTraces(expression string, aggregations []qbtypes.TraceAggregation) string { +// RewriteForTraces rewrites and validates the HAVING expression for a traces query. +func (r *HavingExpressionRewriter) RewriteForTraces(expression string, aggregations []qbtypes.TraceAggregation) (string, error) { + if len(strings.TrimSpace(expression)) == 0 { + return "", nil + } r.buildTraceColumnMap(aggregations) - return r.rewriteExpression(expression) + return r.rewriteAndValidate(expression) } -func (r *HavingExpressionRewriter) RewriteForLogs(expression string, aggregations []qbtypes.LogAggregation) string { +// RewriteForLogs rewrites and validates the HAVING expression for a logs query. +func (r *HavingExpressionRewriter) RewriteForLogs(expression string, aggregations []qbtypes.LogAggregation) (string, error) { + if len(strings.TrimSpace(expression)) == 0 { + return "", nil + } r.buildLogColumnMap(aggregations) - return r.rewriteExpression(expression) + return r.rewriteAndValidate(expression) } -func (r *HavingExpressionRewriter) RewriteForMetrics(expression string, aggregations []qbtypes.MetricAggregation) string { +// RewriteForMetrics rewrites and validates the HAVING expression for a metrics query. +func (r *HavingExpressionRewriter) RewriteForMetrics(expression string, aggregations []qbtypes.MetricAggregation) (string, error) { + if len(strings.TrimSpace(expression)) == 0 { + return "", nil + } r.buildMetricColumnMap(aggregations) - return r.rewriteExpression(expression) + return r.rewriteAndValidate(expression) } func (r *HavingExpressionRewriter) buildTraceColumnMap(aggregations []qbtypes.TraceAggregation) { @@ -45,6 +56,9 @@ func (r *HavingExpressionRewriter) buildTraceColumnMap(aggregations []qbtypes.Tr } r.columnMap[agg.Expression] = sqlColumn + if normalized := strings.ReplaceAll(agg.Expression, " ", ""); normalized != agg.Expression { + r.columnMap[normalized] = sqlColumn + } r.columnMap[fmt.Sprintf("__result%d", idx)] = sqlColumn @@ -65,6 +79,9 @@ func (r *HavingExpressionRewriter) buildLogColumnMap(aggregations []qbtypes.LogA } r.columnMap[agg.Expression] = sqlColumn + if normalized := strings.ReplaceAll(agg.Expression, " ", ""); normalized != agg.Expression { + r.columnMap[normalized] = sqlColumn + } r.columnMap[fmt.Sprintf("__result%d", idx)] = sqlColumn @@ -102,52 +119,3 @@ func (r *HavingExpressionRewriter) buildMetricColumnMap(aggregations []qbtypes.M r.columnMap[fmt.Sprintf("__result%d", idx)] = sqlColumn } } - -func (r *HavingExpressionRewriter) rewriteExpression(expression string) string { - quotedStrings := make(map[string]string) - quotePattern := regexp.MustCompile(`'[^']*'|"[^"]*"`) - quotedIdx := 0 - - expression = quotePattern.ReplaceAllStringFunc(expression, func(match string) string { - placeholder := fmt.Sprintf("__QUOTED_%d__", quotedIdx) - quotedStrings[placeholder] = match - quotedIdx++ - return placeholder - }) - - type mapping struct { - from string - to string - } - - mappings := make([]mapping, 0, len(r.columnMap)) - for from, to := range r.columnMap { - mappings = append(mappings, mapping{from: from, to: to}) - } - - for i := 0; i < len(mappings); i++ { - for j := i + 1; j < len(mappings); j++ { - if len(mappings[j].from) > len(mappings[i].from) { - mappings[i], mappings[j] = mappings[j], mappings[i] - } - } - } - - for _, m := range mappings { - if strings.Contains(m.from, "(") { - // escape special regex characters in the function name - escapedFrom := regexp.QuoteMeta(m.from) - pattern := regexp.MustCompile(`\b` + escapedFrom) - expression = pattern.ReplaceAllString(expression, m.to) - } else { - pattern := regexp.MustCompile(`\b` + regexp.QuoteMeta(m.from) + `\b`) - expression = pattern.ReplaceAllString(expression, m.to) - } - } - - for placeholder, original := range quotedStrings { - expression = strings.Replace(expression, placeholder, original, 1) - } - - return expression -} diff --git a/pkg/querybuilder/having_rewriter_test.go b/pkg/querybuilder/having_rewriter_test.go new file mode 100644 index 000000000..4885b187e --- /dev/null +++ b/pkg/querybuilder/having_rewriter_test.go @@ -0,0 +1,1050 @@ +package querybuilder + +import ( + "testing" + + "github.com/SigNoz/signoz/pkg/errors" + "github.com/SigNoz/signoz/pkg/types/metrictypes" + qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func toTraceAggregations(logs []qbtypes.LogAggregation) []qbtypes.TraceAggregation { + out := make([]qbtypes.TraceAggregation, len(logs)) + for i, l := range logs { + out[i] = qbtypes.TraceAggregation(l) + } + return out +} + +type logsAndTracesTestCase struct { + name string + expression string + aggregations []qbtypes.LogAggregation + wantExpression string + wantErr bool + wantErrMsg string + wantAdditional []string +} + +func runLogsAndTracesTests(t *testing.T, tests []logsAndTracesTestCase) { + t.Helper() + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := NewHavingExpressionRewriter() + traceAggs := toTraceAggregations(tt.aggregations) + gotLogs, errLogs := r.RewriteForLogs(tt.expression, tt.aggregations) + r2 := NewHavingExpressionRewriter() + gotTraces, errTraces := r2.RewriteForTraces(tt.expression, traceAggs) + if tt.wantErr { + require.Error(t, errLogs) + assert.ErrorContains(t, errLogs, tt.wantErrMsg) + _, _, _, _, _, additionalLogs := errors.Unwrapb(errLogs) + assert.Equal(t, tt.wantAdditional, additionalLogs) + require.Error(t, errTraces) + assert.ErrorContains(t, errTraces, tt.wantErrMsg) + _, _, _, _, _, additionalTraces := errors.Unwrapb(errTraces) + assert.Equal(t, tt.wantAdditional, additionalTraces) + } else { + require.NoError(t, errLogs) + assert.Equal(t, tt.wantExpression, gotLogs) + require.NoError(t, errTraces) + assert.Equal(t, tt.wantExpression, gotTraces) + } + }) + } +} + +// TestRewriteForLogsAndTraces_ReferenceTypes covers the different ways an aggregation +// result can be referenced in a HAVING expression: by alias, by expression text, by +// __result shorthand, and by __resultN index. +func TestRewriteForLogsAndTraces_ReferenceTypes(t *testing.T) { + runLogsAndTracesTests(t, []logsAndTracesTestCase{ + { + name: "alias reference", + expression: "total_logs > 1000", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total_logs"}, + }, + wantExpression: "__result_0 > 1000", + }, + { + name: "expression reference", + expression: "sum(bytes) > 1024000", + aggregations: []qbtypes.LogAggregation{ + {Expression: "sum(bytes)"}, + }, + wantExpression: "__result_0 > 1024000", + }, + { + name: "__result reference for single aggregation", + expression: "__result > 500", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "__result_0 > 500", + }, + { + name: "__result0 indexed reference", + expression: "__result0 > 100 AND __result1 < 1000", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + {Expression: "sum(bytes)"}, + }, + wantExpression: "(__result_0 > 100 AND __result_1 < 1000)", + }, + { + name: "__result_0 underscore indexed reference", + expression: "__result_0 > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "__result_0 > 100", + }, + { + name: "reserved keyword as alias", + expression: "sum > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "sum"}, + }, + wantExpression: "__result_0 > 100", + }, + { + name: "comparison between two aggregation references", + expression: "error_count > warn_count AND errors = warnings", + aggregations: []qbtypes.LogAggregation{ + {Expression: "sum(errors)", Alias: "error_count"}, + {Expression: "sum(warnings)", Alias: "warn_count"}, + {Expression: "sum(errors)", Alias: "errors"}, + {Expression: "sum(warnings)", Alias: "warnings"}, + }, + wantExpression: "(__result_0 > __result_1 AND __result_2 = __result_3)", + }, + { + name: "mixed alias and expression reference", + expression: "error_count > 10 AND count() < 1000", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + {Expression: "countIf(level='error')", Alias: "error_count"}, + }, + wantExpression: "(__result_1 > 10 AND __result_0 < 1000)", + }, + }) +} + +// TestRewriteForLogsAndTraces_WhitespaceNormalization verifies that HAVING expression +// references are matched against aggregation expressions in a whitespace-insensitive way. +// +// The column map stores both the original expression and a fully space-stripped version +// as keys. The ANTLR visitor uses ctx.GetText() which also strips all whitespace (WS +// tokens are on a hidden channel). Together these ensure that any spacing difference +// between the aggregation definition and the HAVING reference is tolerated. +func TestRewriteForLogsAndTraces_WhitespaceNormalization(t *testing.T) { + runLogsAndTracesTests(t, []logsAndTracesTestCase{ + { + // Aggregation has space after comma; HAVING reference omits it. + name: "space after comma in multi-arg function", + expression: "count_distinct(a,b) > 10", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count_distinct(a, b)"}, + }, + wantExpression: "__result_0 > 10", + }, + { + // Aggregation has inconsistent spacing around operators; HAVING reference has different inconsistent spacing. + name: "spaces around operators in filter predicate", + expression: "sumIf(a= 'x' or b ='y') > 10", + aggregations: []qbtypes.LogAggregation{ + {Expression: "sumIf(a ='x' or b= 'y')"}, + }, + wantExpression: "__result_0 > 10", + }, + { + // Aggregation has extra spaces inside parens; HAVING reference has none. + name: "spaces in nested function call", + expression: "avg(sum(duration)) > 500", + aggregations: []qbtypes.LogAggregation{ + {Expression: "avg(sum( duration ))"}, + }, + wantExpression: "__result_0 > 500", + }, + { + // Aggregation has no spaces; HAVING reference adds spaces around args. + name: "having adds spaces where aggregation has none", + expression: "count_distinct( a, b ) > 0", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count_distinct(a,b)"}, + }, + wantExpression: "__result_0 > 0", + }, + { + // Multi-arg countIf with a complex AND predicate; both sides use different spacing. + name: "countIf with spaced AND predicate", + expression: "countIf(status='error'AND level='critical') > 0", + aggregations: []qbtypes.LogAggregation{ + {Expression: "countIf(status = 'error' AND level = 'critical')"}, + }, + wantExpression: "__result_0 > 0", + }, + { + // Boolean literals are valid inside function call arguments. + name: "bool literal inside function arg", + expression: "countIf(active=true) > 0", + aggregations: []qbtypes.LogAggregation{ + {Expression: "countIf(active = true)"}, + }, + wantExpression: "__result_0 > 0", + }, + }) +} + +// TestRewriteForLogsAndTraces_BooleanOperators covers explicit AND/OR/NOT, implicit AND +// (adjacent comparisons), parenthesised groups, and associated error cases. +func TestRewriteForLogsAndTraces_BooleanOperators(t *testing.T) { + runLogsAndTracesTests(t, []logsAndTracesTestCase{ + { + name: "implicit AND between two comparisons", + expression: "total > 100 count() < 500", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantExpression: "(__result_0 > 100 AND __result_0 < 500)", + }, + { + name: "complex boolean with parentheses", + expression: "(total > 100 AND avg_duration < 500) OR total > 10000", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + {Expression: "avg(duration)", Alias: "avg_duration"}, + }, + wantExpression: "(((__result_0 > 100 AND __result_1 < 500)) OR __result_0 > 10000)", + }, + { + name: "OR with three operands", + expression: "a > 1 OR b > 2 OR c > 3", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "a"}, + {Expression: "count()", Alias: "b"}, + {Expression: "count()", Alias: "c"}, + }, + wantExpression: "(__result_0 > 1 OR __result_1 > 2 OR __result_2 > 3)", + }, + { + name: "nested parentheses with OR and AND", + expression: "(a > 10 OR b > 20) AND c > 5", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "a"}, + {Expression: "count()", Alias: "b"}, + {Expression: "count()", Alias: "c"}, + }, + wantExpression: "(((__result_0 > 10 OR __result_1 > 20)) AND __result_2 > 5)", + }, + { + name: "NOT on grouped expression", + expression: "NOT (__result_0 > 100 AND __result_1 < 500)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + {Expression: "sum(bytes)"}, + }, + wantExpression: "NOT (__result_0 > 100 AND __result_1 < 500)", + }, + { + name: "NOT with single comparison", + expression: "NOT (total > 100)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantExpression: "NOT __result_0 > 100", + }, + { + name: "NOT without parentheses on function call", + expression: "NOT count() > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "NOT __result_0 > 100", + }, + { + name: "NOT without parentheses on alias", + expression: "NOT total > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantExpression: "NOT __result_0 > 100", + }, + // Error cases + { + name: "double NOT without valid grouping", + expression: "NOT NOT (count() > 100)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:4 expecting one of {'*', '+', '-', (, ), AND, IDENTIFIER, number, string} but got 'NOT'"}, + }, + { + name: "dangling AND at end", + expression: "total_logs > 100 AND", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total_logs"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:20 expecting one of {'*', '+', '-', (, ), AND, IDENTIFIER, NOT, number, string} but got EOF", "Suggestion: `total_logs > 100`"}, + }, + { + name: "dangling OR at start", + expression: "OR total_logs > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total_logs"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:0 expecting one of {'*', '+', '-', (, ), AND, IDENTIFIER, NOT, number, string} but got 'OR'", "Suggestion: `total_logs > 100`"}, + }, + { + name: "dangling OR at end", + expression: "total > 100 OR", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:14 expecting one of {'*', '+', '-', (, ), AND, IDENTIFIER, NOT, number, string} but got EOF", "Suggestion: `total > 100`"}, + }, + { + name: "consecutive AND operators", + expression: "total_logs > 100 AND AND count() < 500", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total_logs"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:21 expecting one of {'*', '+', '-', (, ), IDENTIFIER, NOT, number, string} but got 'AND'"}, + }, + { + name: "AND followed immediately by OR", + expression: "total > 100 AND OR count() < 50", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:16 expecting one of {'*', '+', '-', (, ), AND, IDENTIFIER, NOT, number, string} but got 'OR'"}, + }, + }) +} + +// TestRewriteForLogsAndTraces_UnarySigns covers unary +/- applied to any operand: +// function calls, identifiers, parenthesised groups, and numeric literals. +// +// The NUMBER lexer rule has no leading sign, so a bare `-10` is always tokenised as +// MINUS followed by NUMBER(10). The unary-sign path in the `factor` rule handles it +// identically to `-count()` or `-alias`. As a result, `count()-10 > 0` is valid and +// equivalent to `count() - 10 > 0`: the MINUS is treated as a binary subtraction +// operator between the function-call operand and the literal. +func TestRewriteForLogsAndTraces_UnarySigns(t *testing.T) { + runLogsAndTracesTests(t, []logsAndTracesTestCase{ + { + name: "unary minus on function call", + expression: "-count() > 0", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "-__result_0 > 0", + }, + { + name: "unary plus on function call", + expression: "+count() > 0", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "+__result_0 > 0", + }, + { + name: "unary minus on identifier alias", + expression: "-total > 0", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantExpression: "-__result_0 > 0", + }, + { + name: "unary minus on parenthesised arithmetic", + expression: "-(sum_a + sum_b) > 0", + aggregations: []qbtypes.LogAggregation{ + {Expression: "sum(a)", Alias: "sum_a"}, + {Expression: "sum(b)", Alias: "sum_b"}, + }, + wantExpression: "-(__result_0 + __result_1) > 0", + }, + { + name: "adjacent minus-literal without space is accepted", + expression: "count()-10 > 0", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "__result_0 - 10 > 0", + }, + { + name: "unary minus on indentifier", + expression: "count() < -0", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "__result_0 < -0", + }, + }) +} + +// TestRewriteForLogsAndTraces_Arithmetic covers arithmetic operators (+, -, *, /, %), +// all comparison operators, and numeric literal forms. +func TestRewriteForLogsAndTraces_Arithmetic(t *testing.T) { + runLogsAndTracesTests(t, []logsAndTracesTestCase{ + { + name: "arithmetic on aggregations", + expression: "sum_a + sum_b > 1000", + aggregations: []qbtypes.LogAggregation{ + {Expression: "sum(a)", Alias: "sum_a"}, + {Expression: "sum(b)", Alias: "sum_b"}, + }, + wantExpression: "__result_0 + __result_1 > 1000", + }, + { + name: "comparison operators != <> ==", + expression: "total != 0 AND count() <> 0 AND total == 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantExpression: "(__result_0 != 0 AND __result_0 <> 0 AND __result_0 == 100)", + }, + { + name: "comparison operators < <= > >=", + expression: "total < 100 AND total <= 500 AND total > 10 AND total >= 50", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantExpression: "(__result_0 < 100 AND __result_0 <= 500 AND __result_0 > 10 AND __result_0 >= 50)", + }, + { + name: "numeric literals: negative, float, scientific notation", + expression: "total > -10 AND total > 500.5 AND total > 1e6", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantExpression: "(__result_0 > -10 AND __result_0 > 500.5 AND __result_0 > 1e6)", + }, + { + name: "arithmetic: modulo, subtraction, division, multiplication", + expression: "cnt % 10 = 0 AND cnt - 10 > 0 AND errors / total > 0.05 AND cnt * 2 > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "cnt"}, + {Expression: "sum(errors)", Alias: "errors"}, + {Expression: "count()", Alias: "total"}, + }, + wantExpression: "(__result_0 % 10 = 0 AND __result_0 - 10 > 0 AND __result_1 / __result_2 > 0.05 AND __result_0 * 2 > 100)", + }, + }) +} + +// TestRewriteForLogsAndTraces_QuotedStringKeys covers aggregation expressions that +// contain quoted string arguments (e.g. countIf(level='error')). These keys cannot +// be parsed by the ANTLR grammar directly and are pre-substituted before parsing. +// Both single-quoted and double-quoted variants are tested, including implicit AND. +func TestRewriteForLogsAndTraces_QuotedStringKeys(t *testing.T) { + runLogsAndTracesTests(t, []logsAndTracesTestCase{ + { + // Implicit AND: two adjacent comparisons are joined with AND by the grammar. + // Both single-quoted and double-quoted strings in aggregation expressions are pre-substituted. + name: "quoted string in aggregation expression referenced directly in having", + expression: "countIf(level='error') > 0 countIf(level=\"info\") > 0", + aggregations: []qbtypes.LogAggregation{ + {Expression: "countIf(level='error')"}, + {Expression: `countIf(level="info")`}, + }, + wantExpression: "(__result_0 > 0 AND __result_1 > 0)", + }, + }) +} + +// TestRewriteForLogsAndTraces_InOperator covers IN and NOT IN expressions. +func TestRewriteForLogsAndTraces_InOperator(t *testing.T) { + runLogsAndTracesTests(t, []logsAndTracesTestCase{ + { + name: "IN with numeric list", + expression: "count() IN (1, 2, 3)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "__result_0 IN (1, 2, 3)", + }, + { + name: "NOT IN with numeric list", + expression: "count() NOT IN (1, 2, 3)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "__result_0 NOT IN (1, 2, 3)", + }, + { + name: "IN with bracket list", + expression: "count() IN [1, 2, 3]", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "__result_0 IN (1, 2, 3)", + }, + { + name: "NOT IN with bracket list", + expression: "count() NOT IN [1, 2, 3]", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "__result_0 NOT IN (1, 2, 3)", + }, + { + name: "NOT IN with mixed list", + expression: "count() NOT IN (1,'2', 3)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:18 expecting one of {'*', '+', '-', IDENTIFIER, number} but got ''2''"}, + }, + { + name: "IN via alias", + expression: "total IN (100, 200, 500)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantExpression: "__result_0 IN (100, 200, 500)", + }, + { + name: "NOT IN via alias", + expression: "total NOT IN (0, -1)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantExpression: "__result_0 NOT IN (0, -1)", + }, + { + name: "IN combined with AND", + expression: "count() IN (1, 2, 3) AND sum(bytes) > 1000", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + {Expression: "sum(bytes)"}, + }, + wantExpression: "(__result_0 IN (1, 2, 3) AND __result_1 > 1000)", + }, + { + name: "NOT IN combined with OR", + expression: "count() NOT IN (0, -1) OR sum(bytes) > 1000", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + {Expression: "sum(bytes)"}, + }, + wantExpression: "(__result_0 NOT IN (0, -1) OR __result_1 > 1000)", + }, + { + name: "IN with single value", + expression: "count() IN (42)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "__result_0 IN (42)", + }, + { + name: "IN with unknown reference", + expression: "ghost IN (1, 2, 3)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [ghost]", + wantAdditional: []string{"Valid references are: [__result, __result0, count(), total]"}, + }, + { + name: "IN with end bracked missing", + expression: "count() IN [1, 2, 3", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:19 expecting one of {]} but got EOF", "Suggestion: `count() IN [1, 2, 3]`"}, + }, + { + name: "IN with end paran missing", + expression: "count() IN (1, 2, 3", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:19 expecting one of {)} but got EOF", "Suggestion: `count() IN (1, 2, 3)`"}, + }, + }) +} + +// TestRewriteForLogsAndTraces_EdgeCases covers empty and whitespace-only expressions. +func TestRewriteForLogsAndTraces_EdgeCases(t *testing.T) { + runLogsAndTracesTests(t, []logsAndTracesTestCase{ + { + name: "empty expression", + expression: "", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "", + }, + { + name: "whitespace only expression", + expression: " ", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantExpression: "", + }, + }) +} + +// TestRewriteForLogsAndTraces_ErrorInvalidReferences covers errors produced when the +// expression contains identifiers or function calls that do not match any aggregation. +func TestRewriteForLogsAndTraces_ErrorInvalidReferences(t *testing.T) { + runLogsAndTracesTests(t, []logsAndTracesTestCase{ + { + name: "unknown identifier", + expression: "unknown_alias > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [unknown_alias]", + wantAdditional: []string{"Valid references are: [__result, __result0, count(), total]"}, + }, + { + name: "typo in identifier suggests closest match", + expression: "totol > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [totol]", + wantAdditional: []string{"Valid references are: [__result, __result0, count(), total]", "Suggestion: `total > 100`"}, + }, + { + name: "expression not in column map", + expression: "sum(missing_field) > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [sum]", + wantAdditional: []string{"Valid references are: [__result, __result0, count()]"}, + }, + { + name: "one valid one invalid reference", + expression: "total > 100 AND ghost > 50", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [ghost]", + wantAdditional: []string{"Valid references are: [__result, __result0, count(), total]"}, + }, + { + name: "__result ambiguous with multiple aggregations", + expression: "__result > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + {Expression: "sum(bytes)"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [__result]", + wantAdditional: []string{"Valid references are: [__result0, __result1, count(), sum(bytes)]", "Suggestion: `__result0 > 100`"}, + }, + { + name: "out-of-range __result_N index", + expression: "__result_9 > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [__result_9]", + wantAdditional: []string{"Valid references are: [__result, __result0, count()]", "Suggestion: `__result > 100`"}, + }, + { + name: "__result_1 out of range for single aggregation", + expression: "__result_1 > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [__result_1]", + wantAdditional: []string{"Valid references are: [__result, __result0, count()]", "Suggestion: `__result > 100`"}, + }, + { + name: "cascaded function calls", + expression: "sum(count()) > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [sum]", + wantAdditional: []string{"Valid references are: [__result, __result0, count()]"}, + }, + { + name: "function call with multiple args not in column map", + expression: "sum(a, b) > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "sum(a)"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [sum]", + wantAdditional: []string{"Valid references are: [__result, __result0, sum(a)]"}, + }, + { + name: "unquoted string value treated as unknown identifier", + expression: "sum(bytes) = xyz", + aggregations: []qbtypes.LogAggregation{ + {Expression: "sum(bytes)"}, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [xyz]", + wantAdditional: []string{"Valid references are: [__result, __result0, sum(bytes)]"}, + }, + }) +} + +// TestRewriteForLogsAndTraces_ErrorSyntax covers expressions that produce syntax errors: +// malformed structure (bare operands, mismatched parentheses, missing operators) and +// invalid operand types (string literals, boolean literals). +func TestRewriteForLogsAndTraces_ErrorSyntax(t *testing.T) { + runLogsAndTracesTests(t, []logsAndTracesTestCase{ + // Bare operands + { + name: "bare function call without comparison", + expression: "count()", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total_logs"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:7 expecting one of {'*', '+', '-', (, ), IDENTIFIER, number, string} but got EOF", "Suggestion: `count() > 0`"}, + }, + { + name: "bare identifier without comparison", + expression: "total_logs", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total_logs"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:10 expecting one of {'*', '+', '-', (, ), IDENTIFIER, number, string} but got EOF", "Suggestion: `total_logs > 0`"}, + }, + // Parenthesis mismatches + { + name: "unclosed parenthesis", + expression: "(total_logs > 100 AND count() < 500", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total_logs"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:35 expecting one of {)} but got EOF", "Suggestion: `(total_logs > 100 AND count() < 500)`"}, + }, + { + name: "unexpected closing parenthesis", + expression: "total_logs > 100)", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total_logs"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:16 extraneous input ')' expecting "}, + }, + { + name: "only opening parenthesis", + expression: "(", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:1 expecting one of {'*', '+', '-', (, ), AND, IDENTIFIER, NOT, number, string} but got EOF"}, + }, + { + name: "only closing parenthesis", + expression: ")", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:0 expecting one of {'*', '+', '-', (, AND, IDENTIFIER, NOT, number, string} but got ')'"}, + }, + { + name: "empty parentheses", + expression: "()", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:1 expecting one of {'*', '+', '-', (, AND, IDENTIFIER, NOT, number, string} but got ')'"}, + }, + // Missing operands or operator + { + name: "missing left operand", + expression: "> 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:0 expecting one of {'*', '+', '-', (, ), AND, IDENTIFIER, NOT, number, string} but got '>'; line 1:5 expecting one of {'*', '+', '-', (, ), IDENTIFIER, number, string} but got EOF"}, + }, + { + name: "missing right operand", + expression: "count() >", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:9 expecting one of {'*', '+', '-', (, ), IDENTIFIER, number, string} but got EOF", "Suggestion: `count() > 0`"}, + }, + { + name: "missing comparison operator", + expression: "count() 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:8 expecting one of {'*', '+', '-', (, ), IDENTIFIER, string} but got '100'"}, + }, + // Invalid operand types + { + // BOOL is valid only inside function call args, never as a bare comparison operand. + name: "boolean literal as comparison value", + expression: "count() > true", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:10 expecting one of {'*', '+', '-', (, ), IDENTIFIER, number, string} but got 'true'"}, + }, + { + // false is equally invalid as a comparison operand. + name: "false boolean literal as comparison value", + expression: "count() = false", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()"}, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:10 expecting one of {'*', '+', '-', (, ), IDENTIFIER, number, string} but got 'false'"}, + }, + { + name: "single-quoted string literal as comparison value", + expression: "sum(bytes) = 'xyz'", + aggregations: []qbtypes.LogAggregation{ + {Expression: "sum(bytes)"}, + }, + wantErr: true, + wantErrMsg: "`Having` expression contains string literals", + wantAdditional: []string{"Aggregator results are numeric"}, + }, + { + name: "double-quoted string literal as comparison value", + expression: `total > "threshold"`, + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total"}, + }, + wantErr: true, + wantErrMsg: "`Having` expression contains string literals", + wantAdditional: []string{"Aggregator results are numeric"}, + }, + }) +} + +func TestRewriteForMetrics(t *testing.T) { + tests := []struct { + name string + expression string + aggregations []qbtypes.MetricAggregation + wantExpression string + wantErr bool + wantErrMsg string + wantAdditional []string + }{ + // --- Happy path: reference types (time/space aggregation, __result, bare metric) --- + { + name: "time aggregation reference", + expression: "sum(cpu_usage) > 80", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + TimeAggregation: metrictypes.TimeAggregationSum, + SpaceAggregation: metrictypes.SpaceAggregationUnspecified, + }, + }, + wantExpression: "value > 80", + }, + { + name: "space aggregation reference", + expression: "avg(cpu_usage) > 50", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + SpaceAggregation: metrictypes.SpaceAggregationAvg, + TimeAggregation: metrictypes.TimeAggregationUnspecified, + }, + }, + wantExpression: "value > 50", + }, + { + name: "__result reference", + expression: "__result > 90", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + TimeAggregation: metrictypes.TimeAggregationSum, + SpaceAggregation: metrictypes.SpaceAggregationUnspecified, + }, + }, + wantExpression: "value > 90", + }, + { + name: "bare metric name when no aggregations set", + expression: "cpu_usage > 80", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + TimeAggregation: metrictypes.TimeAggregationUnspecified, + SpaceAggregation: metrictypes.SpaceAggregationUnspecified, + }, + }, + wantExpression: "value > 80", + }, + { + name: "combined space and time aggregation", + expression: "avg(sum(cpu_usage)) > 50", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + TimeAggregation: metrictypes.TimeAggregationSum, + SpaceAggregation: metrictypes.SpaceAggregationAvg, + }, + }, + wantExpression: "value > 50", + }, + // --- Happy path: comparison operators and arithmetic --- + { + name: "comparison operators and arithmetic", + expression: "sum(cpu_usage) < 100 AND sum(cpu_usage) * 2 > 50", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + TimeAggregation: metrictypes.TimeAggregationSum, + SpaceAggregation: metrictypes.SpaceAggregationUnspecified, + }, + }, + wantExpression: "(value < 100 AND value * 2 > 50)", + }, + // --- Happy path: empty or bare operand --- + { + name: "empty expression", + expression: "", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + TimeAggregation: metrictypes.TimeAggregationSum, + SpaceAggregation: metrictypes.SpaceAggregationUnspecified, + }, + }, + wantExpression: "", + }, + // --- Error: invalid or unknown metric reference --- + { + name: "unknown metric reference", + expression: "wrong_metric > 80", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + TimeAggregation: metrictypes.TimeAggregationSum, + SpaceAggregation: metrictypes.SpaceAggregationUnspecified, + }, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [wrong_metric]", + wantAdditional: []string{"Valid references are: [__result, __result0, sum(cpu_usage)]"}, + }, + // --- Error: string literal (not allowed in HAVING) --- + { + name: "string literal rejected", + expression: "cpu_usage = 'high'", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + TimeAggregation: metrictypes.TimeAggregationSum, + SpaceAggregation: metrictypes.SpaceAggregationUnspecified, + }, + }, + wantErr: true, + wantErrMsg: "`Having` expression contains string literals", + wantAdditional: []string{"Aggregator results are numeric"}, + }, + // --- Error: bare operand (no comparison) --- + { + name: "bare operand without comparison", + expression: "cpu_usage", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + TimeAggregation: metrictypes.TimeAggregationSum, + SpaceAggregation: metrictypes.SpaceAggregationUnspecified, + }, + }, + wantErr: true, + wantErrMsg: "Syntax error in `Having` expression", + wantAdditional: []string{"line 1:9 expecting one of {'*', '+', '-', (, ), IDENTIFIER, number, string} but got EOF", "Suggestion: `cpu_usage > 0`"}, + }, + // --- Error: aggregation not in column map --- + { + name: "aggregation not in column map", + expression: "count(cpu_usage) > 10", + aggregations: []qbtypes.MetricAggregation{ + { + MetricName: "cpu_usage", + TimeAggregation: metrictypes.TimeAggregationSum, + SpaceAggregation: metrictypes.SpaceAggregationUnspecified, + }, + }, + wantErr: true, + wantErrMsg: "Invalid references in `Having` expression: [count]", + wantAdditional: []string{"Valid references are: [__result, __result0, sum(cpu_usage)]"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := NewHavingExpressionRewriter() + got, err := r.RewriteForMetrics(tt.expression, tt.aggregations) + if tt.wantErr { + require.Error(t, err) + assert.ErrorContains(t, err, tt.wantErrMsg) + _, _, _, _, _, additional := errors.Unwrapb(err) + assert.Equal(t, tt.wantAdditional, additional) + } else { + require.NoError(t, err) + assert.Equal(t, tt.wantExpression, got) + } + }) + } +} diff --git a/pkg/querybuilder/never_true.go b/pkg/querybuilder/never_true.go index e88bbdada..0b00a6c89 100644 --- a/pkg/querybuilder/never_true.go +++ b/pkg/querybuilder/never_true.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/SigNoz/signoz/pkg/errors" - grammar "github.com/SigNoz/signoz/pkg/parser/grammar" + grammar "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar" qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" "github.com/antlr4-go/antlr/v4" ) diff --git a/pkg/querybuilder/query_to_keys.go b/pkg/querybuilder/query_to_keys.go index 2ac28296a..620c8c5f1 100644 --- a/pkg/querybuilder/query_to_keys.go +++ b/pkg/querybuilder/query_to_keys.go @@ -1,7 +1,7 @@ package querybuilder import ( - grammar "github.com/SigNoz/signoz/pkg/parser/grammar" + grammar "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar" "github.com/SigNoz/signoz/pkg/types/telemetrytypes" "github.com/antlr4-go/antlr/v4" ) diff --git a/pkg/querybuilder/where_clause_visitor.go b/pkg/querybuilder/where_clause_visitor.go index f03fc943d..f804723c2 100644 --- a/pkg/querybuilder/where_clause_visitor.go +++ b/pkg/querybuilder/where_clause_visitor.go @@ -9,7 +9,7 @@ import ( "strings" "github.com/SigNoz/signoz/pkg/errors" - grammar "github.com/SigNoz/signoz/pkg/parser/grammar" + grammar "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar" qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" "github.com/SigNoz/signoz/pkg/types/telemetrytypes" "github.com/antlr4-go/antlr/v4" diff --git a/pkg/querybuilder/where_clause_visitor_test.go b/pkg/querybuilder/where_clause_visitor_test.go index a16393554..e87fdc519 100644 --- a/pkg/querybuilder/where_clause_visitor_test.go +++ b/pkg/querybuilder/where_clause_visitor_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - grammar "github.com/SigNoz/signoz/pkg/parser/grammar" + grammar "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar" qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" "github.com/SigNoz/signoz/pkg/types/telemetrytypes" "github.com/antlr4-go/antlr/v4" diff --git a/pkg/telemetrylogs/filter_expr_logs_test.go b/pkg/telemetrylogs/filter_expr_logs_test.go index 63ff938cc..fe1a2321a 100644 --- a/pkg/telemetrylogs/filter_expr_logs_test.go +++ b/pkg/telemetrylogs/filter_expr_logs_test.go @@ -194,7 +194,7 @@ func TestFilterExprLogs(t *testing.T) { category: "Special characters", query: "ERROR: cannot execute update() in a read-only context", shouldPass: false, - expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), hasToken(), number, quoted text} but got ')'", + expectedErrorContains: "expecting one of {(, AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), hasToken(), number, quoted text} but got ')'", }, { category: "Special characters", @@ -616,7 +616,7 @@ func TestFilterExprLogs(t *testing.T) { shouldPass: false, expectedQuery: "", expectedArgs: []any{}, - expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), hasToken(), number, quoted text} but got 'and'", + expectedErrorContains: "expecting one of {(, ), FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), hasToken(), number, quoted text} but got 'and'", }, { category: "Keyword conflict", @@ -2016,7 +2016,7 @@ func TestFilterExprLogs(t *testing.T) { expectedErrorContains: "", }, - {category: "Only keywords", query: "AND", shouldPass: false, expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), hasToken(), number, quoted text} but got 'AND'"}, + {category: "Only keywords", query: "AND", shouldPass: false, expectedErrorContains: "expecting one of {(, ), FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), hasToken(), number, quoted text} but got 'AND'"}, {category: "Only keywords", query: "OR", shouldPass: false, expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), hasToken(), number, quoted text} but got 'OR'"}, {category: "Only keywords", query: "NOT", shouldPass: false, expectedErrorContains: "expecting one of {(, ), FREETEXT, boolean, has(), hasAll(), hasAny(), hasToken(), number, quoted text} but got EOF"}, @@ -2160,7 +2160,7 @@ func TestFilterExprLogs(t *testing.T) { shouldPass: false, expectedQuery: "", expectedArgs: nil, - expectedErrorContains: "line 1:0 expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), hasToken(), number, quoted text} but got 'and'", + expectedErrorContains: "line 1:0 expecting one of {(, ), FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), hasToken(), number, quoted text} but got 'and'", }, { category: "Operator keywords as keys", diff --git a/pkg/telemetrylogs/statement_builder.go b/pkg/telemetrylogs/statement_builder.go index 20cfdf6f6..2909c75e2 100644 --- a/pkg/telemetrylogs/statement_builder.go +++ b/pkg/telemetrylogs/statement_builder.go @@ -427,7 +427,10 @@ func (b *logQueryStatementBuilder) buildTimeSeriesQuery( if query.Having != nil && query.Having.Expression != "" { // Rewrite having expression to use SQL column names rewriter := querybuilder.NewHavingExpressionRewriter() - rewrittenExpr := rewriter.RewriteForLogs(query.Having.Expression, query.Aggregations) + rewrittenExpr, err := rewriter.RewriteForLogs(query.Having.Expression, query.Aggregations) + if err != nil { + return nil, err + } sb.Having(rewrittenExpr) } @@ -453,7 +456,10 @@ func (b *logQueryStatementBuilder) buildTimeSeriesQuery( sb.GroupBy(querybuilder.GroupByKeys(query.GroupBy)...) if query.Having != nil && query.Having.Expression != "" { rewriter := querybuilder.NewHavingExpressionRewriter() - rewrittenExpr := rewriter.RewriteForLogs(query.Having.Expression, query.Aggregations) + rewrittenExpr, err := rewriter.RewriteForLogs(query.Having.Expression, query.Aggregations) + if err != nil { + return nil, err + } sb.Having(rewrittenExpr) } @@ -560,7 +566,10 @@ func (b *logQueryStatementBuilder) buildScalarQuery( // Add having clause if needed if query.Having != nil && query.Having.Expression != "" { rewriter := querybuilder.NewHavingExpressionRewriter() - rewrittenExpr := rewriter.RewriteForLogs(query.Having.Expression, query.Aggregations) + rewrittenExpr, err := rewriter.RewriteForLogs(query.Having.Expression, query.Aggregations) + if err != nil { + return nil, err + } sb.Having(rewrittenExpr) } diff --git a/pkg/telemetrylogs/stmt_builder_having_test.go b/pkg/telemetrylogs/stmt_builder_having_test.go index da564f95e..883911d1a 100644 --- a/pkg/telemetrylogs/stmt_builder_having_test.go +++ b/pkg/telemetrylogs/stmt_builder_having_test.go @@ -6,6 +6,7 @@ import ( "github.com/SigNoz/signoz/pkg/querybuilder" qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestHavingExpressionRewriter_LogQueries(t *testing.T) { @@ -14,6 +15,7 @@ func TestHavingExpressionRewriter_LogQueries(t *testing.T) { havingExpression string aggregations []qbtypes.LogAggregation expectedExpression string + wantErr bool }{ { name: "single aggregation with alias", @@ -30,7 +32,7 @@ func TestHavingExpressionRewriter_LogQueries(t *testing.T) { {Expression: "count()", Alias: "total"}, {Expression: "avg(duration)", Alias: "avg_duration"}, }, - expectedExpression: "(__result_0 > 100 AND __result_1 < 500) OR __result_0 > 10000", + expectedExpression: "(((__result_0 > 100 AND __result_1 < 500)) OR __result_0 > 10000)", }, { name: "__result reference for single aggregation", @@ -55,7 +57,7 @@ func TestHavingExpressionRewriter_LogQueries(t *testing.T) { {Expression: "count()", Alias: ""}, {Expression: "sum(bytes)", Alias: ""}, }, - expectedExpression: "__result_0 > 100 AND __result_1 < 1000", + expectedExpression: "(__result_0 > 100 AND __result_1 < 1000)", }, { name: "mixed aliases and expressions", @@ -64,15 +66,36 @@ func TestHavingExpressionRewriter_LogQueries(t *testing.T) { {Expression: "count()", Alias: ""}, {Expression: "countIf(level='error')", Alias: "error_count"}, }, - expectedExpression: "__result_1 > 10 AND __result_0 < 1000", + expectedExpression: "(__result_1 > 10 AND __result_0 < 1000)", + }, + { + name: "string literal in having expression", + havingExpression: "count() > 'threshold'", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: ""}, + }, + wantErr: true, + }, + { + name: "unknown reference", + havingExpression: "no_such_alias > 100", + aggregations: []qbtypes.LogAggregation{ + {Expression: "count()", Alias: "total_logs"}, + }, + wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rewriter := querybuilder.NewHavingExpressionRewriter() - result := rewriter.RewriteForLogs(tt.havingExpression, tt.aggregations) - assert.Equal(t, tt.expectedExpression, result) + result, err := rewriter.RewriteForLogs(tt.havingExpression, tt.aggregations) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.expectedExpression, result) + } }) } } diff --git a/pkg/telemetrymetrics/statement_builder.go b/pkg/telemetrymetrics/statement_builder.go index 65e97ffdd..faf3baf23 100644 --- a/pkg/telemetrymetrics/statement_builder.go +++ b/pkg/telemetrymetrics/statement_builder.go @@ -574,7 +574,10 @@ func (b *MetricQueryStatementBuilder) BuildFinalSelect( sb.GroupBy("ts") if query.Having != nil && query.Having.Expression != "" { rewriter := querybuilder.NewHavingExpressionRewriter() - rewrittenExpr := rewriter.RewriteForMetrics(query.Having.Expression, query.Aggregations) + rewrittenExpr, err := rewriter.RewriteForMetrics(query.Having.Expression, query.Aggregations) + if err != nil { + return nil, err + } sb.Having(rewrittenExpr) } } else if metricType == metrictypes.HistogramType && spaceAgg == metrictypes.SpaceAggregationCount && query.Aggregations[0].ComparisonSpaceAggregationParam != nil { @@ -597,7 +600,10 @@ func (b *MetricQueryStatementBuilder) BuildFinalSelect( if query.Having != nil && query.Having.Expression != "" { rewriter := querybuilder.NewHavingExpressionRewriter() - rewrittenExpr := rewriter.RewriteForMetrics(query.Having.Expression, query.Aggregations) + rewrittenExpr, err := rewriter.RewriteForMetrics(query.Having.Expression, query.Aggregations) + if err != nil { + return nil, err + } sb.Having(rewrittenExpr) } } else { @@ -606,7 +612,10 @@ func (b *MetricQueryStatementBuilder) BuildFinalSelect( sb.From("__spatial_aggregation_cte") if query.Having != nil && query.Having.Expression != "" { rewriter := querybuilder.NewHavingExpressionRewriter() - rewrittenExpr := rewriter.RewriteForMetrics(query.Having.Expression, query.Aggregations) + rewrittenExpr, err := rewriter.RewriteForMetrics(query.Having.Expression, query.Aggregations) + if err != nil { + return nil, err + } sb.Where(rewrittenExpr) } } diff --git a/pkg/telemetrytraces/statement_builder.go b/pkg/telemetrytraces/statement_builder.go index 35daebd9c..523a9a2f8 100644 --- a/pkg/telemetrytraces/statement_builder.go +++ b/pkg/telemetrytraces/statement_builder.go @@ -552,7 +552,10 @@ func (b *traceQueryStatementBuilder) buildTimeSeriesQuery( sb.GroupBy(querybuilder.GroupByKeys(query.GroupBy)...) if query.Having != nil && query.Having.Expression != "" { rewriter := querybuilder.NewHavingExpressionRewriter() - rewrittenExpr := rewriter.RewriteForTraces(query.Having.Expression, query.Aggregations) + rewrittenExpr, err := rewriter.RewriteForTraces(query.Having.Expression, query.Aggregations) + if err != nil { + return nil, err + } sb.Having(rewrittenExpr) } @@ -578,7 +581,10 @@ func (b *traceQueryStatementBuilder) buildTimeSeriesQuery( sb.GroupBy(querybuilder.GroupByKeys(query.GroupBy)...) if query.Having != nil && query.Having.Expression != "" { rewriter := querybuilder.NewHavingExpressionRewriter() - rewrittenExpr := rewriter.RewriteForTraces(query.Having.Expression, query.Aggregations) + rewrittenExpr, err := rewriter.RewriteForTraces(query.Having.Expression, query.Aggregations) + if err != nil { + return nil, err + } sb.Having(rewrittenExpr) } @@ -684,7 +690,10 @@ func (b *traceQueryStatementBuilder) buildScalarQuery( // Add having clause if needed if query.Having != nil && query.Having.Expression != "" && !skipHaving { rewriter := querybuilder.NewHavingExpressionRewriter() - rewrittenExpr := rewriter.RewriteForTraces(query.Having.Expression, query.Aggregations) + rewrittenExpr, err := rewriter.RewriteForTraces(query.Having.Expression, query.Aggregations) + if err != nil { + return nil, err + } sb.Having(rewrittenExpr) } diff --git a/pkg/telemetrytraces/trace_id_extractor.go b/pkg/telemetrytraces/trace_id_extractor.go index e1ed94843..48d0bf018 100644 --- a/pkg/telemetrytraces/trace_id_extractor.go +++ b/pkg/telemetrytraces/trace_id_extractor.go @@ -3,7 +3,7 @@ package telemetrytraces import ( "strings" - grammar "github.com/SigNoz/signoz/pkg/parser/grammar" + grammar "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar" "github.com/antlr4-go/antlr/v4" ) diff --git a/pkg/telemetrytraces/trace_operator_cte_builder.go b/pkg/telemetrytraces/trace_operator_cte_builder.go index a1c702648..53ac2cec2 100644 --- a/pkg/telemetrytraces/trace_operator_cte_builder.go +++ b/pkg/telemetrytraces/trace_operator_cte_builder.go @@ -624,7 +624,10 @@ func (b *traceOperatorCTEBuilder) buildTimeSeriesQuery(ctx context.Context, sele combinedArgs := append(allGroupByArgs, allAggChArgs...) // Add HAVING clause if specified - b.addHavingClause(sb) + err = b.addHavingClause(sb) + if err != nil { + return nil, err + } sql, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse, combinedArgs...) return &qbtypes.Statement{ @@ -740,7 +743,10 @@ func (b *traceOperatorCTEBuilder) buildTraceQuery(ctx context.Context, selectFro sb.GroupBy(groupByKeys...) } - b.addHavingClause(sb) + err = b.addHavingClause(sb) + if err != nil { + return nil, err + } orderApplied := false for _, orderBy := range b.operator.Order { @@ -883,7 +889,10 @@ func (b *traceOperatorCTEBuilder) buildScalarQuery(ctx context.Context, selectFr combinedArgs := append(allGroupByArgs, allAggChArgs...) // Add HAVING clause if specified - b.addHavingClause(sb) + err = b.addHavingClause(sb) + if err != nil { + return nil, err + } sql, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse, combinedArgs...) return &qbtypes.Statement{ @@ -892,12 +901,16 @@ func (b *traceOperatorCTEBuilder) buildScalarQuery(ctx context.Context, selectFr }, nil } -func (b *traceOperatorCTEBuilder) addHavingClause(sb *sqlbuilder.SelectBuilder) { +func (b *traceOperatorCTEBuilder) addHavingClause(sb *sqlbuilder.SelectBuilder) error { if b.operator.Having != nil && b.operator.Having.Expression != "" { rewriter := querybuilder.NewHavingExpressionRewriter() - rewrittenExpr := rewriter.RewriteForTraces(b.operator.Having.Expression, b.operator.Aggregations) + rewrittenExpr, err := rewriter.RewriteForTraces(b.operator.Having.Expression, b.operator.Aggregations) + if err != nil { + return err + } sb.Having(rewrittenExpr) } + return nil } func (b *traceOperatorCTEBuilder) addCTE(name, sql string, args []any, dependsOn []string) { diff --git a/pkg/types/querybuildertypes/querybuildertypesv5/validation.go b/pkg/types/querybuildertypes/querybuildertypesv5/validation.go index 99ad9d26f..e80cb7b80 100644 --- a/pkg/types/querybuildertypes/querybuildertypesv5/validation.go +++ b/pkg/types/querybuildertypes/querybuildertypesv5/validation.go @@ -264,6 +264,12 @@ func (q *QueryBuilderQuery[T]) validateAggregations(cfg validationConfig) error } aliases[v.Alias] = true } + if strings.Contains(strings.ToLower(v.Expression), " as ") { + return errors.NewInvalidInputf( + errors.CodeInvalidInput, + "aliasing is not allowed in expression. Use `alias` field instead", + ) + } case LogAggregation: if v.Expression == "" { aggId := fmt.Sprintf("aggregation #%d", i+1) @@ -286,6 +292,12 @@ func (q *QueryBuilderQuery[T]) validateAggregations(cfg validationConfig) error } aliases[v.Alias] = true } + if strings.Contains(strings.ToLower(v.Expression), " as ") { + return errors.NewInvalidInputf( + errors.CodeInvalidInput, + "aliasing is not allowed in expression. Use `alias` field instead", + ) + } } } diff --git a/pkg/variables/variable_replace_visitor.go b/pkg/variables/variable_replace_visitor.go index 06ffa21ff..a62d48acb 100644 --- a/pkg/variables/variable_replace_visitor.go +++ b/pkg/variables/variable_replace_visitor.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/SigNoz/signoz/pkg/errors" - grammar "github.com/SigNoz/signoz/pkg/parser/grammar" + grammar "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar" qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" "github.com/antlr4-go/antlr/v4" ) diff --git a/scripts/grammar/generate-go-parser.sh b/scripts/grammar/generate-go-parser.sh index b46169d4b..6723343e5 100755 --- a/scripts/grammar/generate-go-parser.sh +++ b/scripts/grammar/generate-go-parser.sh @@ -6,6 +6,7 @@ echo "Generating Go parser..." mkdir -p pkg/parser # Generate Go parser -antlr -visitor -Dlanguage=Go -o pkg/parser grammar/FilterQuery.g4 +antlr -visitor -Dlanguage=Go -o pkg/parser/filterquery grammar/FilterQuery.g4 +antlr -visitor -Dlanguage=Go -o pkg/parser/havingexpression grammar/HavingExpression.g4 echo "Go parser generation complete"