-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[KQL Query] Create the ANTLR parser #114927
Changes from all commits
5debc71
d00b7db
13152e2
7421ea4
460220c
e712d9d
ec22015
eb12243
c46ea5c
aa02224
1742ddc
3db7002
64a87e5
cc7434a
a2b78c3
43d85c8
54c060d
a09a83f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import org.elasticsearch.gradle.internal.info.BuildParams | ||
|
||
apply plugin: 'elasticsearch.internal-es-plugin' | ||
apply plugin: 'elasticsearch.internal-cluster-test' | ||
apply plugin: 'elasticsearch.publish' | ||
|
||
esplugin { | ||
name 'x-pack-kql' | ||
description 'Elasticsearch Expanded Pack Plugin - KQL query' | ||
classname 'org.elasticsearch.xpack.kql.KqlPlugin' | ||
extendedPlugins = ['x-pack-core'] | ||
} | ||
base { | ||
archivesName = 'x-pack-kql' | ||
} | ||
|
||
dependencies { | ||
compileOnly project(path: xpackModule('core')) | ||
compileOnly "org.antlr:antlr4-runtime:${versions.antlr4}" | ||
|
||
testImplementation "org.antlr:antlr4-runtime:${versions.antlr4}" | ||
testImplementation project(':test:framework') | ||
testImplementation(testArtifact(project(xpackModule('core')))) | ||
} | ||
|
||
/**************************************************************** | ||
* Enable QA/rest integration tests for snapshot builds only * | ||
* TODO: Enable for all builds upon this feature release * | ||
****************************************************************/ | ||
if (BuildParams.isSnapshotBuild()) { | ||
addQaCheckDependencies(project) | ||
} | ||
|
||
/********************************** | ||
* KQL parser configuration * | ||
**********************************/ | ||
configurations { | ||
regenerate | ||
} | ||
|
||
dependencies { | ||
regenerate "org.antlr:antlr4:${versions.antlr4}" | ||
} | ||
|
||
String grammarPath = 'src/main/antlr' | ||
String outputPath = 'src/main/java/org/elasticsearch/xpack/kql/parser' | ||
|
||
pluginManager.withPlugin('com.diffplug.spotless') { | ||
spotless { | ||
java { | ||
// for some reason "${outputPath}/KqlBaser*.java" does not match the same files... | ||
targetExclude "src/main/java/org/elasticsearch/xpack/kql/parser/KqlBase*.java" | ||
} | ||
} | ||
} | ||
tasks.named('checkstyleMain').configure { | ||
exclude { it.file.toString().contains("src/main/java/org/elasticsearch/xpack/kql/parser/KqlBase") } | ||
} | ||
|
||
tasks.register("cleanGenerated", Delete) { | ||
delete fileTree(grammarPath) { | ||
include '*.tokens' | ||
} | ||
delete fileTree(outputPath) { | ||
include 'KqlBase*.java' | ||
include 'KqlBase*.interp' | ||
} | ||
} | ||
|
||
tasks.register("regenParser", JavaExec) { | ||
dependsOn "cleanGenerated" | ||
mainClass = 'org.antlr.v4.Tool' | ||
classpath = configurations.regenerate | ||
systemProperty 'file.encoding', 'UTF-8' | ||
systemProperty 'user.language', 'en' | ||
systemProperty 'user.country', 'US' | ||
systemProperty 'user.variant', '' | ||
args '-Werror', | ||
'-package', 'org.elasticsearch.xpack.kql.parser', | ||
'-listener', | ||
'-visitor', | ||
'-o', outputPath, | ||
"${file(grammarPath)}/KqlBase.g4" | ||
} | ||
|
||
tasks.register("regen") { | ||
dependsOn "regenParser" | ||
doLast { | ||
// moves token files to grammar directory for use with IDE's | ||
ant.move(file: "${outputPath}/KqlBase.tokens", toDir: grammarPath) | ||
ant.move(file: "${outputPath}/KqlBaseLexer.tokens", toDir: grammarPath) | ||
// make the generated classes package private | ||
ant.replaceregexp(match: 'public ((interface|class) \\QKqlBase\\E\\w+)', | ||
replace: '\\1', | ||
encoding: 'UTF-8') { | ||
fileset(dir: outputPath, includes: 'KqlBase*.java') | ||
} | ||
// nuke timestamps/filenames in generated files | ||
ant.replaceregexp(match: '\\Q// Generated from \\E.*', | ||
replace: '\\/\\/ ANTLR GENERATED CODE: DO NOT EDIT', | ||
encoding: 'UTF-8') { | ||
fileset(dir: outputPath, includes: 'KqlBase*.java') | ||
} | ||
// remove tabs in antlr generated files | ||
ant.replaceregexp(match: '\t', flags: 'g', replace: ' ', encoding: 'UTF-8') { | ||
fileset(dir: outputPath, includes: 'KqlBase*.java') | ||
} | ||
// fix line endings | ||
ant.fixcrlf(srcdir: outputPath, eol: 'lf') { | ||
patternset(includes: 'KqlBase*.java') | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
grammar KqlBase; | ||
|
||
|
||
@header { | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
} | ||
|
||
options { | ||
caseInsensitive=true; | ||
} | ||
|
||
topLevelQuery | ||
: query? EOF | ||
; | ||
|
||
query | ||
: query (AND | OR) query #booleanQuery | ||
| NOT subQuery=simpleQuery #notQuery | ||
| simpleQuery #defaultQuery | ||
; | ||
|
||
simpleQuery | ||
: nestedQuery | ||
| expression | ||
| parenthesizedQuery | ||
; | ||
|
||
expression | ||
: fieldTermQuery | ||
| fieldRangeQuery | ||
; | ||
|
||
nestedQuery | ||
: fieldName COLON LEFT_CURLY_BRACKET query RIGHT_CURLY_BRACKET | ||
; | ||
|
||
parenthesizedQuery: | ||
LEFT_PARENTHESIS query RIGHT_PARENTHESIS; | ||
|
||
fieldRangeQuery | ||
: fieldName operator=OP_COMPARE rangeQueryValue | ||
; | ||
|
||
fieldTermQuery | ||
: (fieldName COLON)? termQueryValue | ||
; | ||
|
||
fieldName | ||
: wildcardExpression | ||
| unquotedLiteralExpression | ||
| quotedStringExpression | ||
; | ||
|
||
rangeQueryValue | ||
: unquotedLiteralExpression | ||
| quotedStringExpression | ||
; | ||
|
||
termQueryValue | ||
: wildcardExpression | ||
| quotedStringExpression | ||
| termValue=unquotedLiteralExpression | ||
| groupingTermExpression; | ||
|
||
groupingTermExpression | ||
: LEFT_PARENTHESIS unquotedLiteralExpression RIGHT_PARENTHESIS | ||
; | ||
|
||
unquotedLiteralExpression | ||
: UNQUOTED_LITERAL+ | ||
; | ||
|
||
quotedStringExpression | ||
: QUOTED_STRING | ||
; | ||
|
||
wildcardExpression | ||
: WILDCARD | ||
; | ||
|
||
|
||
DEFAULT_SKIP: WHITESPACE -> skip; | ||
|
||
AND: 'and'; | ||
OR: 'or'; | ||
NOT: 'not'; | ||
|
||
COLON: ':'; | ||
OP_COMPARE: OP_LESS | OP_MORE | OP_LESS_EQ | OP_MORE_EQ; | ||
|
||
LEFT_PARENTHESIS: '('; | ||
RIGHT_PARENTHESIS: ')'; | ||
LEFT_CURLY_BRACKET: '{'; | ||
RIGHT_CURLY_BRACKET: '}'; | ||
|
||
UNQUOTED_LITERAL: WILDCARD* UNQUOTED_LITERAL_CHAR+ WILDCARD*; | ||
|
||
QUOTED_STRING: '"'QUOTED_CHAR*'"'; | ||
|
||
WILDCARD: WILDCARD_CHAR+; | ||
|
||
fragment WILDCARD_CHAR: '*'; | ||
fragment OP_LESS: '<'; | ||
fragment OP_LESS_EQ: '<='; | ||
fragment OP_MORE: '>'; | ||
fragment OP_MORE_EQ: '>='; | ||
|
||
fragment UNQUOTED_LITERAL_CHAR | ||
: ESCAPED_WHITESPACE | ||
| ESCAPED_SPECIAL_CHAR | ||
| ESCAPE_UNICODE_SEQUENCE | ||
| '\\' (AND | OR | NOT) | ||
| WILDCARD_CHAR UNQUOTED_LITERAL_CHAR | ||
| NON_SPECIAL_CHAR | ||
; | ||
|
||
fragment QUOTED_CHAR | ||
: ESCAPED_WHITESPACE | ||
| ESCAPE_UNICODE_SEQUENCE | ||
| ESCAPED_QUOTE | ||
| ~["] | ||
; | ||
|
||
fragment WHITESPACE: [ \t\n\r\u3000]; | ||
fragment ESCAPED_WHITESPACE: '\\r' | '\\t' | '\\n'; | ||
fragment NON_SPECIAL_CHAR: ~[ \\():<>"*{}]; | ||
fragment ESCAPED_SPECIAL_CHAR: '\\'[ \\():<>"*{}]; | ||
|
||
fragment ESCAPED_QUOTE: '\\"'; | ||
|
||
fragment ESCAPE_UNICODE_SEQUENCE: '\\' UNICODE_SEQUENCE; | ||
fragment UNICODE_SEQUENCE: 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT; | ||
fragment HEX_DIGIT: [0-9a-f]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
DEFAULT_SKIP=1 | ||
AND=2 | ||
OR=3 | ||
NOT=4 | ||
COLON=5 | ||
OP_COMPARE=6 | ||
LEFT_PARENTHESIS=7 | ||
RIGHT_PARENTHESIS=8 | ||
LEFT_CURLY_BRACKET=9 | ||
RIGHT_CURLY_BRACKET=10 | ||
UNQUOTED_LITERAL=11 | ||
QUOTED_STRING=12 | ||
WILDCARD=13 | ||
'and'=2 | ||
'or'=3 | ||
'not'=4 | ||
':'=5 | ||
'('=7 | ||
')'=8 | ||
'{'=9 | ||
'}'=10 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
DEFAULT_SKIP=1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
AND=2 | ||
OR=3 | ||
NOT=4 | ||
COLON=5 | ||
OP_COMPARE=6 | ||
LEFT_PARENTHESIS=7 | ||
RIGHT_PARENTHESIS=8 | ||
LEFT_CURLY_BRACKET=9 | ||
RIGHT_CURLY_BRACKET=10 | ||
UNQUOTED_LITERAL=11 | ||
QUOTED_STRING=12 | ||
WILDCARD=13 | ||
'and'=2 | ||
'or'=3 | ||
'not'=4 | ||
':'=5 | ||
'('=7 | ||
')'=8 | ||
'{'=9 | ||
'}'=10 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.kql; | ||
|
||
import org.elasticsearch.plugins.ExtensiblePlugin; | ||
import org.elasticsearch.plugins.Plugin; | ||
import org.elasticsearch.plugins.SearchPlugin; | ||
|
||
public class KqlPlugin extends Plugin implements SearchPlugin, ExtensiblePlugin { | ||
carlosdelest marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.kql.parser; | ||
|
||
import org.antlr.v4.runtime.ParserRuleContext; | ||
import org.elasticsearch.index.query.MatchAllQueryBuilder; | ||
import org.elasticsearch.index.query.QueryBuilder; | ||
import org.elasticsearch.index.query.SearchExecutionContext; | ||
|
||
class KqlAstBuilder extends KqlBaseBaseVisitor<QueryBuilder> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ℹ️ The KqlAstBuilder will transform the AST into a query builder. The actual implementation will come in a later PR (the current behavior is to always return a match_all query). |
||
private final SearchExecutionContext searchExecutionContext; | ||
|
||
KqlAstBuilder(SearchExecutionContext searchExecutionContext) { | ||
this.searchExecutionContext = searchExecutionContext; | ||
} | ||
|
||
public QueryBuilder toQueryBuilder(ParserRuleContext ctx) { | ||
if (ctx instanceof KqlBaseParser.TopLevelQueryContext topLeveQueryContext) { | ||
return new MatchAllQueryBuilder(); | ||
} | ||
|
||
throw new IllegalArgumentException("context should be of type TopLevelQueryContext"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.