Java 语义谓词
我正在尝试让语义谓词工作。这看起来很直截了当,但不知何故不起作用,基于一个布尔条件,我需要执行一个吐出AST的规则,或者只是或手动构造一个AST 下面是解析器规则Java 语义谓词,java,antlr,antlr3,Java,Antlr,Antlr3,我正在尝试让语义谓词工作。这看起来很直截了当,但不知何故不起作用,基于一个布尔条件,我需要执行一个吐出AST的规则,或者只是或手动构造一个AST 下面是解析器规则 displayed_column : {columnAliases.containsKey($text)}? =>-> ^(PROPERTY_LIST ^(PROPERTY IDENTIFIER[columnAliases.get($text)])) | sql_expression
displayed_column
:
{columnAliases.containsKey($text)}?
=>-> ^(PROPERTY_LIST ^(PROPERTY IDENTIFIER[columnAliases.get($text)]))
| sql_expression
;
我也尝试了所有的门控和消歧,但在运行代码时,它总是转到第二个规则sql_表达式
谁能帮帮我吗
谢谢
编辑:
我刚刚意识到$text在谓词运行时是空的,这就是为什么它总是匹配第二条规则。我把规则改成了这个,它起作用了
displayed_column
:
sql_expression
-> {columnAliases.containsKey($text)}? ^(PROPERTY_LIST ^(PROPERTY IDENTIFIER[columnAliases.get($text)]))
-> sql_expression
但是我现在遇到了一个不同的问题,我意识到手动构建树将不起作用,我需要使用ColumnAlias映射中的新文本值重新运行规则显示列,这可能吗
这是我最初的问题
基本上,我试图以交互方式解析和解释类似sql的语句
例:
由于列名可能有点长,我通过不同的命令为用户提供了别名列名的首选项,例如,用户可以设置首选项,然后运行他的命令
set column_alias a.b.c d;
select d from pool;
现在,在解析时,我将首选项映射注入生成的解析器中,并尝试将新列替换/映射回原始列,然后继续解释。
对我来说,在解析器中处理它似乎是唯一的选择,因为我认为使用树语法很难做到这一点,因为该列跨越多个规则
我可以发布整个语法,但它有点太长了,这里是它的缩小版
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和一个解释它的树语法。
如果有人能告诉我哪种方式不那么麻烦,我将不胜感激
再次感谢。与其将字符串存储为值,为什么不在地图中存储实际的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();};
如果您正在使用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。与其将字符串存储为值,为什么不将实际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();};
如果您正在使用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。您要么在第一个选项中不匹配任何内容,要么在第二个选项中匹配sql\u表达式。因此,如果您的映射假设它是一个包含空字符串作为键的映射,则不匹配任何内容只会使ColumnAlias.containsKey$text计算为true。你能提供更多的背景吗?可能会给出一些输入字符串和所需AST作为输出的示例吗?嗨,巴特,我已经为问题添加了更多细节。第一个选项没有匹配任何内容,或者第二个选项匹配sql_表达式。因此,如果您的映射假设它是一个包含空字符串作为键的映射,则不匹配任何内容只会使ColumnAlias.containsKey$text计算为true。你能提供更多的背景吗?可能会给出一些示例输入字符串和所需的AST作为输出?嗨,巴特,我已经为问题添加了更多细节。谢谢巴特!唯一的问题是我需要将这些首选项保存在数据存储中,这样用户就不需要再次输入它们,希望我可以序列化CommonTree。@jack_carver,没问题。也看到我的编辑。谢谢巴特!唯一的问题是我需要将这些首选项保存在数据存储中,这样用户就不需要再次输入它们,希望我可以序列化CommonTree。@jack_carver,没问题。另请参见我的编辑。