我正在尝试获取语义谓词。这似乎是直截了当但不知何故不起作用,基于一个布尔条件,我需要执行一个规则(吐出一个AST)或只是或手动构建一个
下面的是解析器规则。
displayed_column
:
{columnAliases.containsKey($text)}?
=>-> ^(PROPERTY_LIST ^(PROPERTY IDENTIFIER[columnAliases.get($text)]))
| sql_expression
;
我也尝试了所有的gated和歧义,但在运行代码时,它总是转到第二个规则(sql_expression)。
有人可以帮帮我吗?
由于
修改 我刚刚意识到$ text是空的,而谓词正在运行,这就是为什么它总是匹配第二个规则。我将规则更改为此并且可以正常工作
displayed_column
:
sql_expression
-> {columnAliases.containsKey($text)}? ^(PROPERTY_LIST ^(PROPERTY IDENTIFIER[columnAliases.get($text)]))
-> sql_expression
然而,我现在遇到了一个不同的问题,我意识到手动构建树将无法工作,我需要使用新文本(columnAliases Map中的值)重新运行规则displayed_column,这可能吗?
这是我原来的问题 https://stackoverflow.com/questions/14170541/antlr-dynamic-input-stream-modification-during-parsing
基本上我试图以交互方式解析和解释sql之类的语句 例如:
select a.b.c from pool;
select min(abc.def[*]) from pool;
由于列名可能有点长,我已经给用户优先选择别名列名(通过不同的命令),例如用户可能设置首选项然后运行他的命令
set column_alias a.b.c d;
select d from pool;
现在,在解析时,我将首选项(Map)注入到生成的解析器中,我试图将新列替换/映射回原始列,然后继续解释。 在解析器中处理它似乎是我唯一的选择,因为我认为由于该列跨越多个规则而很难用树语法。
我可以发布整个语法,但它有点太长了,这里是缩小版的
select_stmt:
: 'select' displayed_column 'from' pool
;
displayed_column
: sql_expression
;
sql_expression
: term ( (PLUS^ | MINUS^) term)*
;
term : factor ( (ASTERISK^ | DIVIDE^) factor)*
;
... <more_rules> ...
我坚持这一点,使用字符串模板输出翻译的语句,然后重新分析似乎是我唯一的选择,但这需要重写整个语法到输出模板(现在我有一个组合语法与输出AST和解释它的树语法)。 如果有人能告诉我哪种方式不那么具有干扰性,我们将不胜感激。
再次感谢。
答案 0 :(得分:4)
为什么不将实际的AST存储在地图中,而不是将字符串存储为值?然后可以通过将{
... }
包装在重写规则中来注入这些AST。
演示:
grammar T;
options {
output=AST;
ASTLabelType=CommonTree;
}
tokens {
STATS;
DISPLAYED_COLUMN;
NAME;
SELECT;
}
@parser::header {
import java.util.Map;
import java.util.HashMap;
}
@parser::members {
private Map<String, CommonTree> aliases = new HashMap<String, CommonTree>();
}
parse
: (stmt ';')+ EOF -> ^(STATS stmt+)
;
stmt
: set_stmt
| select_stmt
;
set_stmt
: 'set' 'alias' name Id {aliases.put($Id.text, $name.tree);} -> /* AST can be omitted */
;
select_stmt
: 'select' displayed_column 'from' name -> ^(SELECT displayed_column name)
;
displayed_column
: sql_expression -> {aliases.containsKey($text)}? ^(DISPLAYED_COLUMN {aliases.get($text)})
-> ^(DISPLAYED_COLUMN sql_expression)
;
sql_expression
: term (('+' | '-')^ term)*
;
term
: factor (('*' | '/')^ factor)*
;
factor
: Num
| name
| '(' sql_expression ')'
;
name
: Id ('.' Id)* -> ^(NAME Id+)
;
Id : 'a'..'z'+;
Num : '0'..'9'+;
Space : (' ' | '\t' | '\r' | '\n')+ {skip();};
解析输入:
select d from pool; set alias a.b.c d; select d from pool;
将导致以下AST:
感谢巴特!唯一的问题是我需要在数据存储中保留这些首选项,以便用户不需要再次重新输入它们,希望我能序列化CommonTree。
:(唉,它不是Serializable。
在这种情况下,您可以将值存储为字符串,并使用小辅助方法createNameAST(String alias)
动态创建AST并注入此方法创建的AST:
grammar T;
options {
output=AST;
ASTLabelType=CommonTree;
}
tokens {
STATS;
DISPLAYED_COLUMN;
NAME;
SELECT;
}
@parser::header {
import java.util.Map;
import java.util.HashMap;
}
@parser::members {
private Map<String, String> aliases = new HashMap<String, String>();
private CommonTree createNameAST(String alias) {
try {
TLexer lexer = new TLexer(new ANTLRStringStream(aliases.get(alias)));
TParser parser = new TParser(new CommonTokenStream(lexer));
return (CommonTree)parser.name().getTree();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
parse
: (stmt ';')+ EOF -> ^(STATS stmt+)
;
stmt
: set_stmt
| select_stmt
;
set_stmt
: 'set' 'alias' name Id {aliases.put($Id.text, $name.text);} -> /* AST can be omitted */
;
select_stmt
: 'select' displayed_column 'from' name -> ^(SELECT displayed_column name)
;
displayed_column
: sql_expression -> {aliases.containsKey($text)}? ^(DISPLAYED_COLUMN {createNameAST($text)})
-> ^(DISPLAYED_COLUMN sql_expression)
;
sql_expression
: term (('+' | '-')^ term)*
;
term
: factor (('*' | '/')^ factor)*
;
factor
: Num
| name
| '(' sql_expression ')'
;
name
: Id ('.' Id)* -> ^(NAME Id+)
;
Id : 'a'..'z'+;
Num : '0'..'9'+;
Space : (' ' | '\t' | '\r' | '\n')+ {skip();};
如果您使用的是ANTLRWorks中的调试器:方法createNameAST
可能存在问题,因为它使用TParser
。手动创建一个小测试用例:
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;
public class Main {
public static void main(String[] args) throws Exception {
String src =
"select d from pool; \n" +
"set alias a.b.c.x d; \n" +
"select d from pool;";
TLexer lexer = new TLexer(new ANTLRStringStream(src));
TParser parser = new TParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)parser.parse().getTree();
DOTTreeGenerator gen = new DOTTreeGenerator();
StringTemplate st = gen.toDOT(tree);
System.out.println(st);
}
}
并在命令行上运行全部:
java -cp antlr-3.3.jar org.antlr.Tool T.g javac -cp antlr-3.3.jar *.java java -cp .:antlr-3.3.jar Main > ast.dot
你会得到一个代表之前发布的相同AST的DOT文件。