我是ANTLR的新手,正在尝试支持逻辑表达式。我的问题是,语法甚至将单个'&'视为逻辑与(与|和OR相同),情况不应该是这样的。
这是我的语法文件(TagQuery.g4):
query
: (expression)+
;
expression
: keyValue #comparisonExpression
| inExpression #singleInExpression
| hasExpression #singleHasExpression
| expressionGroup #singleExpressionGroup
| expression logicalOperator expression #logicalExpression
| expressionGroup logicalOperator expressionGroup #expressionGroupExpression
;
inExpression
: key IN literalList
;
hasExpression
: HAS key
;
expressionGroup
: NOT? L_RND_BRACKET (expression)+ R_RND_BRACKET
;
keyValue
: key comparisonOperator literal
;
key
: KEY_PREFIX (keyName) R_RND_BRACKET
;
keyName
: IDENTIFIER
;
comparisonOperator
: EQ | NEQ
;
logicalOperator
: AND | OR
;
literalList
: L_SQ_BRACKET literal (COMMA literal)* R_SQ_BRACKET
;
literal
: STRING_LITERAL
;
// keywords
AND : ' '*('&&' | 'AND' | 'and')' '*;
OR : ' '*('||' | 'OR' | 'or')' '*;
IDENTIFIER : [A-Za-z0-9_]+ ;
ESCAPED_IDENTIFIER : '"' ( '"' '"' | [^"] )* '"';
STRING_LITERAL : ''' [A-Za-z0-9_ ]* ''';
NOT : ' '*('!' | 'not' | 'NOT')' '*;
HAS : ' '*('HAS' | 'has')' '*;
IN : ' '*('IN' | 'in')' '*;
EQ : ' '*'='' '*;
NEQ : ' '*'!='' '*;
L_SQ_BRACKET : ' '*'['' '*;
R_SQ_BRACKET : ' '*']'' '*;
L_RND_BRACKET : ' '*'('' '*;
R_RND_BRACKET : ' '*')'' '*;
COMMA : ' '*','' '*;
KEY_PREFIX : 'tags('' '*;
//KEY_SUFFIX : ')'' '*;
SPACE : [ trn]+ -> channel(HIDDEN);
WS : [ trn]+ -> channel(HIDDEN);
//BLANK : ' '*;
一个有效的查询示例如下:tags(environment) = 'prod' && tags(region) = 'syd'
下面是计算查询的方法:
public static Filter forQuery(String query) throws ParseException {
try {
LOGGER.info("Attempting to get tag filter for query {}", query);
CharStream in = CharStreams.fromStream(new ByteArrayInputStream(query.getBytes()));
TagQueryLexer lexer = new TagQueryLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
TagQueryParser parser = new TagQueryParser(tokens);
TagQueryListener listener = new TagQueryListener();
parser.addParseListener(listener);
TagQueryParseErrorListener errorListener = new TagQueryParseErrorListener();
parser.addErrorListener(errorListener);
parser.query();
if (errorListener.getException() != null) {
listener.getFilter().setParseError(errorListener.getException());
LOGGER.error("Tag filter for query {} returned an error. Please check query syntax n {}", query, errorListener.getException());
throw errorListener.getException();
}
LOGGER.info("Tag filter for query {} returned successfully", query);
return listener.getFilter();
} catch (IOException e) {
LOGGER.error("Unexpected error encountered while getting filter for query {} n {}", query, e);
throw new RuntimeException(e);
}
}
下面是一些无效的查询:
(tags(project) = 'cloudcollectors' & tags(jira) = 'XTNXBL')
(tags(project) = 'cloudcollectors' | tags(jira) = 'XTNXBL')
(tags(project) = 'cloudcollectors' tags(jira) = 'XTNXBL')
(tags(project) = 'cloudcollectors' tags(jira) = 'XTNXBL')
(tags(project) = 'cloudcollectors'tags(jira) = 'XTNXBL')
我希望errorListener在上面的方法抛出一个异常,因为这些查询语法不正确。然而,这并没有发生,它们继续返回一个Filter对象。
谁能建议一下如何通过这个?我是不是在语法上遗漏了什么?谢谢你!
正如@kaby76指出的那样,您将得到一个令牌识别错误,这来自Lexer。您还需要将ErrorListener
附加到Lexer。
lexer.addErrorListener(errorListener);
另外,您确实希望ErrorListener收集它遇到的所有错误。只是询问errorListener.getException()
(可能)只是要给你它遇到的最后一个错误。
当ANTLR遇到syntaxError时,它将在所有附加的侦听器上启用syntaxError(...)
方法并继续处理。您可以决定如何对报告给侦听器的语法错误作出反应。
你可以选择如何处理你的异常,但是"normal"过程将是建立一个ArrayList
的所有错误,你遇到并使其在TagQueryListener
类访问。然后你可以添加方法来检查错误,检查最大严重程度,检查错误计数等(你也可以选择在遇到第一个错误时抛出一个Exception来中止处理,但这不会是一个很好的用户体验。)
(我同意他的建议re:空白处理)