Skip to content

Commit 6f60880

Browse files
authored
[KQL Query] Create the ANTLR parser (#114927)
1 parent 0779983 commit 6f60880

19 files changed

+3033
-0
lines changed

x-pack/plugin/kql/build.gradle

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import org.elasticsearch.gradle.internal.info.BuildParams
2+
3+
apply plugin: 'elasticsearch.internal-es-plugin'
4+
apply plugin: 'elasticsearch.internal-cluster-test'
5+
apply plugin: 'elasticsearch.publish'
6+
7+
esplugin {
8+
name 'x-pack-kql'
9+
description 'Elasticsearch Expanded Pack Plugin - KQL query'
10+
classname 'org.elasticsearch.xpack.kql.KqlPlugin'
11+
extendedPlugins = ['x-pack-core']
12+
}
13+
base {
14+
archivesName = 'x-pack-kql'
15+
}
16+
17+
dependencies {
18+
compileOnly project(path: xpackModule('core'))
19+
compileOnly "org.antlr:antlr4-runtime:${versions.antlr4}"
20+
21+
testImplementation "org.antlr:antlr4-runtime:${versions.antlr4}"
22+
testImplementation project(':test:framework')
23+
testImplementation(testArtifact(project(xpackModule('core'))))
24+
}
25+
26+
/****************************************************************
27+
* Enable QA/rest integration tests for snapshot builds only *
28+
* TODO: Enable for all builds upon this feature release *
29+
****************************************************************/
30+
if (BuildParams.isSnapshotBuild()) {
31+
addQaCheckDependencies(project)
32+
}
33+
34+
/**********************************
35+
* KQL parser configuration *
36+
**********************************/
37+
configurations {
38+
regenerate
39+
}
40+
41+
dependencies {
42+
regenerate "org.antlr:antlr4:${versions.antlr4}"
43+
}
44+
45+
String grammarPath = 'src/main/antlr'
46+
String outputPath = 'src/main/java/org/elasticsearch/xpack/kql/parser'
47+
48+
pluginManager.withPlugin('com.diffplug.spotless') {
49+
spotless {
50+
java {
51+
// for some reason "${outputPath}/KqlBaser*.java" does not match the same files...
52+
targetExclude "src/main/java/org/elasticsearch/xpack/kql/parser/KqlBase*.java"
53+
}
54+
}
55+
}
56+
tasks.named('checkstyleMain').configure {
57+
exclude { it.file.toString().contains("src/main/java/org/elasticsearch/xpack/kql/parser/KqlBase") }
58+
}
59+
60+
tasks.register("cleanGenerated", Delete) {
61+
delete fileTree(grammarPath) {
62+
include '*.tokens'
63+
}
64+
delete fileTree(outputPath) {
65+
include 'KqlBase*.java'
66+
include 'KqlBase*.interp'
67+
}
68+
}
69+
70+
tasks.register("regenParser", JavaExec) {
71+
dependsOn "cleanGenerated"
72+
mainClass = 'org.antlr.v4.Tool'
73+
classpath = configurations.regenerate
74+
systemProperty 'file.encoding', 'UTF-8'
75+
systemProperty 'user.language', 'en'
76+
systemProperty 'user.country', 'US'
77+
systemProperty 'user.variant', ''
78+
args '-Werror',
79+
'-package', 'org.elasticsearch.xpack.kql.parser',
80+
'-listener',
81+
'-visitor',
82+
'-o', outputPath,
83+
"${file(grammarPath)}/KqlBase.g4"
84+
}
85+
86+
tasks.register("regen") {
87+
dependsOn "regenParser"
88+
doLast {
89+
// moves token files to grammar directory for use with IDE's
90+
ant.move(file: "${outputPath}/KqlBase.tokens", toDir: grammarPath)
91+
ant.move(file: "${outputPath}/KqlBaseLexer.tokens", toDir: grammarPath)
92+
// make the generated classes package private
93+
ant.replaceregexp(match: 'public ((interface|class) \\QKqlBase\\E\\w+)',
94+
replace: '\\1',
95+
encoding: 'UTF-8') {
96+
fileset(dir: outputPath, includes: 'KqlBase*.java')
97+
}
98+
// nuke timestamps/filenames in generated files
99+
ant.replaceregexp(match: '\\Q// Generated from \\E.*',
100+
replace: '\\/\\/ ANTLR GENERATED CODE: DO NOT EDIT',
101+
encoding: 'UTF-8') {
102+
fileset(dir: outputPath, includes: 'KqlBase*.java')
103+
}
104+
// remove tabs in antlr generated files
105+
ant.replaceregexp(match: '\t', flags: 'g', replace: ' ', encoding: 'UTF-8') {
106+
fileset(dir: outputPath, includes: 'KqlBase*.java')
107+
}
108+
// fix line endings
109+
ant.fixcrlf(srcdir: outputPath, eol: 'lf') {
110+
patternset(includes: 'KqlBase*.java')
111+
}
112+
}
113+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
grammar KqlBase;
9+
10+
11+
@header {
12+
/*
13+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
14+
* or more contributor license agreements. Licensed under the Elastic License
15+
* 2.0; you may not use this file except in compliance with the Elastic License
16+
* 2.0.
17+
*/
18+
}
19+
20+
options {
21+
caseInsensitive=true;
22+
}
23+
24+
topLevelQuery
25+
: query? EOF
26+
;
27+
28+
query
29+
: query (AND | OR) query #booleanQuery
30+
| NOT subQuery=simpleQuery #notQuery
31+
| simpleQuery #defaultQuery
32+
;
33+
34+
simpleQuery
35+
: nestedQuery
36+
| expression
37+
| parenthesizedQuery
38+
;
39+
40+
expression
41+
: fieldTermQuery
42+
| fieldRangeQuery
43+
;
44+
45+
nestedQuery
46+
: fieldName COLON LEFT_CURLY_BRACKET query RIGHT_CURLY_BRACKET
47+
;
48+
49+
parenthesizedQuery:
50+
LEFT_PARENTHESIS query RIGHT_PARENTHESIS;
51+
52+
fieldRangeQuery
53+
: fieldName operator=OP_COMPARE rangeQueryValue
54+
;
55+
56+
fieldTermQuery
57+
: (fieldName COLON)? termQueryValue
58+
;
59+
60+
fieldName
61+
: wildcardExpression
62+
| unquotedLiteralExpression
63+
| quotedStringExpression
64+
;
65+
66+
rangeQueryValue
67+
: unquotedLiteralExpression
68+
| quotedStringExpression
69+
;
70+
71+
termQueryValue
72+
: wildcardExpression
73+
| quotedStringExpression
74+
| termValue=unquotedLiteralExpression
75+
| groupingTermExpression;
76+
77+
groupingTermExpression
78+
: LEFT_PARENTHESIS unquotedLiteralExpression RIGHT_PARENTHESIS
79+
;
80+
81+
unquotedLiteralExpression
82+
: UNQUOTED_LITERAL+
83+
;
84+
85+
quotedStringExpression
86+
: QUOTED_STRING
87+
;
88+
89+
wildcardExpression
90+
: WILDCARD
91+
;
92+
93+
94+
DEFAULT_SKIP: WHITESPACE -> skip;
95+
96+
AND: 'and';
97+
OR: 'or';
98+
NOT: 'not';
99+
100+
COLON: ':';
101+
OP_COMPARE: OP_LESS | OP_MORE | OP_LESS_EQ | OP_MORE_EQ;
102+
103+
LEFT_PARENTHESIS: '(';
104+
RIGHT_PARENTHESIS: ')';
105+
LEFT_CURLY_BRACKET: '{';
106+
RIGHT_CURLY_BRACKET: '}';
107+
108+
UNQUOTED_LITERAL: WILDCARD* UNQUOTED_LITERAL_CHAR+ WILDCARD*;
109+
110+
QUOTED_STRING: '"'QUOTED_CHAR*'"';
111+
112+
WILDCARD: WILDCARD_CHAR+;
113+
114+
fragment WILDCARD_CHAR: '*';
115+
fragment OP_LESS: '<';
116+
fragment OP_LESS_EQ: '<=';
117+
fragment OP_MORE: '>';
118+
fragment OP_MORE_EQ: '>=';
119+
120+
fragment UNQUOTED_LITERAL_CHAR
121+
: ESCAPED_WHITESPACE
122+
| ESCAPED_SPECIAL_CHAR
123+
| ESCAPE_UNICODE_SEQUENCE
124+
| '\\' (AND | OR | NOT)
125+
| WILDCARD_CHAR UNQUOTED_LITERAL_CHAR
126+
| NON_SPECIAL_CHAR
127+
;
128+
129+
fragment QUOTED_CHAR
130+
: ESCAPED_WHITESPACE
131+
| ESCAPE_UNICODE_SEQUENCE
132+
| ESCAPED_QUOTE
133+
| ~["]
134+
;
135+
136+
fragment WHITESPACE: [ \t\n\r\u3000];
137+
fragment ESCAPED_WHITESPACE: '\\r' | '\\t' | '\\n';
138+
fragment NON_SPECIAL_CHAR: ~[ \\():<>"*{}];
139+
fragment ESCAPED_SPECIAL_CHAR: '\\'[ \\():<>"*{}];
140+
141+
fragment ESCAPED_QUOTE: '\\"';
142+
143+
fragment ESCAPE_UNICODE_SEQUENCE: '\\' UNICODE_SEQUENCE;
144+
fragment UNICODE_SEQUENCE: 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT;
145+
fragment HEX_DIGIT: [0-9a-f];
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
DEFAULT_SKIP=1
2+
AND=2
3+
OR=3
4+
NOT=4
5+
COLON=5
6+
OP_COMPARE=6
7+
LEFT_PARENTHESIS=7
8+
RIGHT_PARENTHESIS=8
9+
LEFT_CURLY_BRACKET=9
10+
RIGHT_CURLY_BRACKET=10
11+
UNQUOTED_LITERAL=11
12+
QUOTED_STRING=12
13+
WILDCARD=13
14+
'and'=2
15+
'or'=3
16+
'not'=4
17+
':'=5
18+
'('=7
19+
')'=8
20+
'{'=9
21+
'}'=10
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
DEFAULT_SKIP=1
2+
AND=2
3+
OR=3
4+
NOT=4
5+
COLON=5
6+
OP_COMPARE=6
7+
LEFT_PARENTHESIS=7
8+
RIGHT_PARENTHESIS=8
9+
LEFT_CURLY_BRACKET=9
10+
RIGHT_CURLY_BRACKET=10
11+
UNQUOTED_LITERAL=11
12+
QUOTED_STRING=12
13+
WILDCARD=13
14+
'and'=2
15+
'or'=3
16+
'not'=4
17+
':'=5
18+
'('=7
19+
')'=8
20+
'{'=9
21+
'}'=10
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.kql;
9+
10+
import org.elasticsearch.plugins.ExtensiblePlugin;
11+
import org.elasticsearch.plugins.Plugin;
12+
import org.elasticsearch.plugins.SearchPlugin;
13+
14+
public class KqlPlugin extends Plugin implements SearchPlugin, ExtensiblePlugin {
15+
16+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.kql.parser;
9+
10+
import org.antlr.v4.runtime.ParserRuleContext;
11+
import org.elasticsearch.index.query.MatchAllQueryBuilder;
12+
import org.elasticsearch.index.query.QueryBuilder;
13+
import org.elasticsearch.index.query.SearchExecutionContext;
14+
15+
class KqlAstBuilder extends KqlBaseBaseVisitor<QueryBuilder> {
16+
private final SearchExecutionContext searchExecutionContext;
17+
18+
KqlAstBuilder(SearchExecutionContext searchExecutionContext) {
19+
this.searchExecutionContext = searchExecutionContext;
20+
}
21+
22+
public QueryBuilder toQueryBuilder(ParserRuleContext ctx) {
23+
if (ctx instanceof KqlBaseParser.TopLevelQueryContext topLeveQueryContext) {
24+
return new MatchAllQueryBuilder();
25+
}
26+
27+
throw new IllegalArgumentException("context should be of type TopLevelQueryContext");
28+
}
29+
}

0 commit comments

Comments
 (0)