Antlr AST树对复杂语法的研究

时间:2012-11-28 15:02:02

标签: java antlr

我写了一个复杂的语法。语法如下:

grammar i;

options {
output=AST;
}

@header {
package com.data;
}

operatorLogic   : 'AND' | 'OR';
value       : STRING;
query       : (select)*;
select      : 'SELECT'^ functions 'FROM table' filters?';';
operator    : '=' | '!=' | '<' | '>' | '<=' | '>=';
filters : 'WHERE'^ conditions;
conditions  : (members (operatorLogic members)*);
members : STRING operator value;
functions   : '*';
STRING  : ('a'..'z'|'A'..'Z')+;
WS      : (' '|'\t'|'\f'|'\n'|'\r')+ {skip();}; // handle white space between keywords

使用AST完成输出。以上只是一个小样本。但是,我正在开发一些大语法,需要有关如何处理这个问题的建议。

例如,根据上述语法,可以产生以下内容:

SELECT * from table;
SELECT * from table WHERE name = i AND name = j;

此查询可能会变得更复杂。我已经在Java代码中实现了AST,并且可以将Tree恢复。我想分离语法和逻辑,所以它们是有凝聚力的。所以AST是最好的方法。

用户将以字符串形式输入查询,我的代码需要以尽可能最好的方式处理查询。正如您所看到的,函数解析器当前是*,这意味着选择all。在未来,这可以扩展到包括其他东西。

我的代码如何处理这个问题?什么是最好的方法?

我可以这样做:

String input = "SELECT * from table;";
if(input.startsWith("SELECT")) {
    select();
}

正如您所看到的,这种方法更复杂,因为我需要处理*也是可选的过滤器。还需要完成AND和OR的operatorLogic。

最好的方法是什么?我已经在线查看,但找不到任何关于如何处理这个问题的例子。

你能提供任何例子吗?

编辑:

String input = "SELECT * FROM table;";
if(input.startsWith("SELECT")) {
   select();
}
else if(input.startsWith("SELECT *")) {
  findAll();
}

1 个答案:

答案 0 :(得分:0)

处理多个启动规则(“SELECT ...”,“UPDATE ...”等)的最简单方法是让ANTLR语法在单个顶级启动规则中为您完成工作。你已经拥有了它,所以只需要更新你拥有的东西。

目前你的语法仅限于一个命令类型的输入(“SELECT ...”),因为这是你所定义的:

query       : (select)*; //query only handles "select" because that's all there is.
select      : 'SELECT'^ functions 'FROM table' filters?';';

如果query是您的起始规则,那么接受额外的顶级输入就是将query定义为接受select以上的内容:

query       : (select | update)*; //query now handles any number of "select" or "update" rules, in any order.
select      : 'SELECT'^ functions 'FROM table' filters?';';
update      : 'UPDATE'^ ';';  //simple example of an update rule

现在query规则可以处理SELECT * FROM table;UPDATE;SELECT * FROM table; UPDATE;等输入。添加新的顶级规则后,只需更新query即可测试该新规则。这样您的Java代码就不需要测试输入,它只调用query规则并让解析器处理其余的。

如果您只想从输入处理一种类型的输入,请像这样定义query

query       : select*  //read any number of selects, but no updates
            | update*  //read any number of updates, but no selects
            ;

规则query仍会处理SELECT * FROM table;UPDATE;,但不会混合使用SELECT * FROM table; UPDATE;等命令。


从调用query_return获得query AST树后,您现在可以使用Java代码可以处理的有意义的内容,而不是字符串。该树表示解析器处理的所有输入。

你可以像这样走过树的孩子:

iParser.query_return r = parser.query();
CommonTree t = (CommonTree) r.getTree();

for (int i = 0, count = t.getChildCount(); i < count; ++i) {
    CommonTree child = (CommonTree) t.getChild(i);
    System.out.println("child type: " + child.getType());
    System.out.println("child text: " + child.getText());
    System.out.println("------");
}

遍历整个AST树是在所有父节点上递归调用getChild(...)的问题(上面的示例仅查看顶级子节点)。


处理*的替代方案与您定义的任何其他方案没有什么不同:只需在要扩展的规则中定义备选方案即可。如果您希望functions接受*以上的内容,请定义functions以接受*以上的内容。 ;)

以下是一个例子:

functions: '*'      //"all"
         | STRING   //some id
         ;

现在,解析器可以接受SELECT * FROM table;SELECT foobar FROM table;

请记住,您的Java代码无理由来检查输入字符串。每当你想要这样做时,寻找一种方法让你的语法做检查。然后,您的Java代码将根据需要查看AST树输出。