0

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

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;

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

用户将以字符串形式输入查询,我的代码需要以最佳方式处理查询。如您所见,函数解析器当前是 * 表示全选。将来,这可能会扩展到包括其他事物。

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

我可以做这样的事情:

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();
}
4

1 回答 1

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;and UPDATE;,但不是混合命令,例如SELECT * FROM table; UPDATE;.


一旦你query_return从调用中得到你的 AST 树query,你现在就有了一些有意义的东西,你的 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;and SELECT foobar FROM table;

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

于 2012-11-28T15:41:29.040 回答