blob: 7842c1b2fefd5e341307c4e8ca3db21253e1849c [file] [log] [blame]
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
grammar Query;
options {
language = Java;
output = AST;
}
tokens {
FIELD_NAME;
DEFAULT_FIELD;
SINGLE_WORD;
EXACT_PHRASE;
AND;
OR;
NOT;
}
@header {
package com.google.gerrit.server.query;
}
@members {
static class QueryParseInternalException extends RuntimeException {
private static final long serialVersionUID = 1L;
QueryParseInternalException(final String msg) {
super(msg);
}
}
public static Tree parse(final String str)
throws QueryParseException {
try {
final QueryParser p = new QueryParser(
new TokenRewriteStream(
new QueryLexer(
new ANTLRStringStream(str)
)
)
);
return (Tree)p.query().getTree();
} catch (QueryParseInternalException e) {
throw new QueryParseException(e.getMessage());
} catch (RecognitionException e) {
throw new QueryParseException(e.getMessage());
}
}
static boolean isSingleWord(final String value) {
try {
final QueryLexer lexer = new QueryLexer(new ANTLRStringStream(value));
lexer.mSINGLE_WORD();
return lexer.nextToken().getType() == QueryParser.EOF;
} catch (RecognitionException e) {
return false;
}
}
@Override
public void displayRecognitionError(String[] tokenNames,
RecognitionException e) {
String hdr = getErrorHeader(e);
String msg = getErrorMessage(e, tokenNames);
throw new QueryParseInternalException(hdr + " " + msg);
}
}
@lexer::header {
package com.google.gerrit.server.query;
}
@lexer::members {
}
query
: conditionOr
;
conditionOr
: (conditionAnd OR)
=> conditionAnd OR^ conditionAnd (OR! conditionAnd)*
| conditionAnd
;
conditionAnd
: (conditionNot AND)
=> i+=conditionNot (i+=conditionAnd2)*
-> ^(AND $i+)
| (conditionNot conditionNot)
=> i+=conditionNot (i+=conditionAnd2)*
-> ^(AND $i+)
| conditionNot
;
conditionAnd2
: AND! conditionNot
| conditionNot
;
conditionNot
: '-' conditionBase -> ^(NOT conditionBase)
| NOT^ conditionBase
| conditionBase
;
conditionBase
: (FIELD_NAME ':') => FIELD_NAME^ ':'! fieldValue
| fieldValue -> ^(DEFAULT_FIELD fieldValue)
;
fieldValue
: n=FIELD_NAME -> SINGLE_WORD[n]
| SINGLE_WORD
| EXACT_PHRASE
| '('! conditionOr ')'!
;
AND: 'AND' ;
OR: 'OR' ;
NOT: 'NOT' ;
WS
: ( ' ' | '\r' | '\t' | '\n' ) { $channel=HIDDEN; }
;
FIELD_NAME
: ('a'..'z')+
;
EXACT_PHRASE
: '"' ( ~('"') )* '"' {
String s = $text;
setText(s.substring(1, s.length() - 1));
}
;
SINGLE_WORD
: ~( '-' | NON_WORD ) ( ~( NON_WORD ) )*
;
fragment NON_WORD
: ( '\u0000'..' '
| '!'
| '"'
| '#'
| '$'
| '%'
| '&'
| '\''
| '(' | ')'
// '*' permit
// '+' permit
// ',' permit
// '-' permit
// '.' permit
// '/' permit
| ':'
| ';'
| '<' | '=' | '>'
| '?'
| '[' | ']'
| '{' | '}'
| '~'
)
;