grammar FilterQuery; /* * Parser Rules */ query : expression EOF ; // Expression with standard boolean precedence: // - parentheses > NOT > AND > OR // - consecutive expressions with no AND/OR => implicit AND expression : orExpression ; // OR expressions orExpression : andExpression ( OR andExpression )* ; // AND expressions + optional chaining with implicit AND if no OR is present andExpression : unaryExpression ( AND unaryExpression | unaryExpression )* ; // A unary expression handles optional NOT unaryExpression : NOT? primary ; // Primary constructs: grouped expressions, a comparison (key op value), // a function call, or a full-text string primary : LPAREN orExpression RPAREN | comparison | functionCall | fullText | key | value ; /* * Comparison-like filters * * Includes all operators: =, !=, <>, <, <=, >, >=, [NOT] LIKE, [NOT] ILIKE, * [NOT] BETWEEN, [NOT] IN, [NOT] EXISTS, [NOT] REGEXP, [NOT] CONTAINS, etc. */ comparison : key EQUALS value | key (NOT_EQUALS | NEQ) value | key LT value | key LE value | key GT value | key GE value | key (LIKE | ILIKE) value | key (NOT_LIKE | NOT_ILIKE) value | key BETWEEN value AND value | key NOT BETWEEN value AND value | key inClause | key notInClause | key EXISTS | key NOT EXISTS | key REGEXP value | key NOT REGEXP value | key CONTAINS value | key NOT CONTAINS value ; // in(...) or in[...] inClause : IN LPAREN valueList RPAREN | IN LBRACK valueList RBRACK ; notInClause : NOT IN LPAREN valueList RPAREN | NOT IN LBRACK valueList RBRACK ; // List of values for in(...) or in[...] valueList : value ( COMMA value )* ; // Full-text search: a standalone quoted string is allowed as a "primary" // e.g. `"Waiting for response" http.status_code=200` fullText : QUOTED_TEXT | FREETEXT ; /* * Function calls like: * has(payload.user_ids, 123) * hasAny(payload.user_ids, [123, 456]) * ... */ functionCall : (HAS | HASANY | HASALL) LPAREN functionParamList RPAREN ; // Function parameters can be keys, single scalar values, or arrays functionParamList : functionParam ( COMMA functionParam )* ; functionParam : key | value | array ; // An array: [ item1, item2, item3 ] array : LBRACK valueList RBRACK ; /* * A 'value' can be a string literal (double or single-quoted), // a numeric literal, boolean, or a "bare" token as needed. */ value : QUOTED_TEXT | NUMBER | BOOL | KEY ; /* * A key can include letters, digits, underscores, dots, brackets * E.g. service.name, query_log.query_duration_ms, proto.user_objects[].name */ key : KEY ; /* * Lexer Rules */ // Common punctuation / symbols LPAREN : '(' ; RPAREN : ')' ; LBRACK : '[' ; RBRACK : ']' ; COMMA : ',' ; EQUALS : '=' | '==' ; NOT_EQUALS : '!=' ; NEQ : '<>' ; // alternate not-equals operator LT : '<' ; LE : '<=' ; GT : '>' ; GE : '>=' ; // Operators that are made of multiple keywords LIKE : [Ll][Ii][Kk][Ee] ; NOT_LIKE : [Nn][Oo][Tt] [ \t]+ [Ll][Ii][Kk][Ee] ; ILIKE : [Ii][Ll][Ii][Kk][Ee] ; NOT_ILIKE : [Nn][Oo][Tt] [ \t]+ [Ii][Ll][Ii][Kk][Ee] ; BETWEEN : [Bb][Ee][Tt][Ww][Ee][Ee][Nn] ; EXISTS : [Ee][Xx][Ii][Ss][Tt][Ss]? ; REGEXP : [Rr][Ee][Gg][Ee][Xx][Pp] ; CONTAINS : [Cc][Oo][Nn][Tt][Aa][Ii][Nn][Ss]? ; IN : [Ii][Nn] ; // Boolean logic NOT : [Nn][Oo][Tt] ; AND : [Aa][Nn][Dd] ; OR : [Oo][Rr] ; // For easy referencing in function calls HAS : [Hh][Aa][Ss] ; HASANY : [Hh][Aa][Ss][Aa][Nn][Yy] ; HASALL : [Hh][Aa][Ss][Aa][Ll][Ll] ; // Potential boolean constants BOOL : [Tt][Rr][Uu][Ee] | [Ff][Aa][Ll][Ss][Ee] ; fragment SIGN : [+-] ; // Numbers: optional sign, then digits, optional fractional part, // optional scientific notation (handy for future use) NUMBER : SIGN? DIGIT+ ('.' DIGIT*)? ([eE] SIGN? DIGIT+)? // -10.25 42 +3.14 6.02e23 | SIGN? '.' DIGIT+ ([eE] SIGN? DIGIT+)? // -.75 .5 -.5e-3 ; // Double/single-quoted text, capturing full text search strings, values, etc. QUOTED_TEXT : ( '"' ( ~["\\] | '\\' . )* '"' // double-quoted | '\'' ( ~['\\] | '\\' . )* '\'' // single-quoted ) ; fragment SEGMENT : [a-zA-Z] [a-zA-Z0-9_:\-]* ; fragment EMPTY_BRACKS : '[' ']' ; fragment OLD_JSON_BRACKS: '[' '*' ']'; KEY : SEGMENT ( '.' SEGMENT | EMPTY_BRACKS | OLD_JSON_BRACKS)* ; // Ignore whitespace WS : [ \t\r\n]+ -> skip ; // Digits used by NUMBER fragment DIGIT : [0-9] ; FREETEXT : (~[ \t\r\n=()'"<>!,[\]])+ ;