Java 语义谓词

Java 语义谓词,java,antlr,antlr3,Java,Antlr,Antlr3,我正在尝试让语义谓词工作。这看起来很直截了当,但不知何故不起作用,基于一个布尔条件,我需要执行一个吐出AST的规则,或者只是或手动构造一个AST 下面是解析器规则 displayed_column : {columnAliases.containsKey($text)}? =>-> ^(PROPERTY_LIST ^(PROPERTY IDENTIFIER[columnAliases.get($text)])) | sql_expression

我正在尝试让语义谓词工作。这看起来很直截了当,但不知何故不起作用,基于一个布尔条件,我需要执行一个吐出AST的规则,或者只是或手动构造一个AST

下面是解析器规则

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,没问题。另请参见我的编辑。