ANTLR 4和AST访客

Gli*_*ome 10 java antlr visitor abstract-syntax-tree antlr4

我正在尝试将AST与ANTLR4一起使用,包含以下文件:

Builder.java

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.TokenStream;

public class Builder
{

    public static void main(String[] args)
    {
        CharStream input = new ANTLRInputStream("ON M1==2 && M3 == 5 && (M2 > 1 || M5 <= 5.0) "
                                              + "DO P5:42 P4:10");
        ExprLexer lexer = new ExprLexer(input);
        TokenStream tokens = new CommonTokenStream(lexer);
        ExprParser parser = new ExprParser(tokens);
        parser.addParseListener(new ExprTestListener());
        ExprParser.ExpressionContext uu = parser.expression();
    }

}
Run Code Online (Sandbox Code Playgroud)

ExprTestListener:

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.ErrorNode;

public class ExprTestListener extends ExprBaseListener {
    @Override public void enterExpression(ExprParser.ExpressionContext ctx)
    {
        System.out.println(ctx);
    }
    @Override public void exitExpression(ExprParser.ExpressionContext ctx)
    {
        System.out.println(ctx);
    }

    @Override public void enterActionexpr(ExprParser.ActionexprContext ctx)
    {
        System.out.println(ctx);
    }
    @Override public void exitActionexpr(ExprParser.ActionexprContext ctx)
    {
        System.out.println(ctx);
    }

    @Override public void enterCondexpr(ExprParser.CondexprContext ctx)
    {
        System.out.println(ctx);
    }
    @Override public void exitCondexpr(ExprParser.CondexprContext ctx)
    {
        System.out.println(ctx);
    }

    @Override public void enterCond(ExprParser.CondContext ctx)
    {
        System.out.println(ctx);
    }
    @Override public void exitCond(ExprParser.CondContext ctx)
    {
        System.out.println(ctx);
    }

    @Override public void enterEveryRule(ParserRuleContext ctx)
    {
        System.out.println(ctx);
    }
    @Override public void exitEveryRule(ParserRuleContext ctx)
    {
        System.out.println(ctx);
    }
    @Override public void visitTerminal(TerminalNode node)
    {
    }
    @Override public void visitErrorNode(ErrorNode node)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

Expr.g:

grammar Expr;
options
{
  // antlr will generate java lexer and parser
  language = Java;

}
WS      : [ \t\r\n]+ -> skip ;
OP      : '&&' | '||';
COMP    : '==' | '<' | '>' | '<=' | '>=' | '!=';
fragment INT     : [0-9]+;
REAL    : INT '.' INT | INT;

ACTION  : 'P' INT ':' INT;
MEASURE : 'M' INT;

// ***************** parser rules:
cond       : MEASURE COMP REAL;
condexpr   : '(' condexpr ')' | cond OP condexpr | cond;
actionexpr : ACTION actionexpr | ACTION;
expression : 'ON' condexpr 'DO' actionexpr;
Run Code Online (Sandbox Code Playgroud)

我有这个输出:

[]
[]
[29]
[29]
[16 29]
[16 29]
[16 29]
[16 29]
[18 29]
[18 29]
[16 18 29]
[16 18 29]
[16 18 29]
[16 18 29]
[18 18 29]
[18 18 29]
[13 18 18 29]
[13 18 18 29]
[16 13 18 18 29]
[16 13 18 18 29]
[16 13 18 18 29]
[16 13 18 18 29]
[18 13 18 18 29]
[18 13 18 18 29]
[20 18 13 18 18 29]
[20 18 13 18 18 29]
[20 18 13 18 18 29]
[20 18 13 18 18 29]
[18 13 18 18 29]
[18 13 18 18 29]
[13 18 18 29]
[13 18 18 29]
[18 18 29]
[18 18 29]
[18 29]
[18 29]
[29]
[29]
[31]
[31]
[24 31]
[24 31]
[24 31]
[24 31]
[31]
[31]
[]
[]
Run Code Online (Sandbox Code Playgroud)

我发现用ANTLR4很难理解访客.

我有树目标:

  • 获取MEASURE和ACTION的INT(两组不同)
  • 替换一些OP(例如!= by <>)
  • 获取带有OPs remplaced的condexpr(顶部项)字符串(请参阅前一点)

Sam*_*ell 11

首先,我将解释您在上面观察到的内容:

首先,请阅读您调用的方法的文档.该Parser.addParseListener文档包括以下注释:

这仅适用于高级用户.请将ParseTreeListener提供给ParseTreeWalker,而不是将其提供给解析器!

实行toString()ParserRuleContext类简单地打印规则调用栈的情况下被创建的时间.当收听者输入规则时,以及退出时,您将打印一次.对于actionexpr,condcondexpr您再次打印,导致每个上下文总共有4个相同的输出行.

现在关于你的目标的一些注释:

  • 在里面enterCondexitCond,MEASURE文本可以通过调用ctx.MEASURE().getText().
  • 在里面enterActionexprexitActionexpr,ACTION文本可以通过调用ctx.ACTION().getText().
  • 您可以更改COND通过创建一个新的令牌TerminalNodeImpl,并CommonToken在更新的令牌,并在现场将其分配给正确的索引CondContext.children使用一个访问者或监听.


Tux*_*uin 1

您可以使用树标签来设置解析的上下文,然后使用访问者类和触发方法遍历观察图的叶子,以便从语言源代码中的表达式创建操作。因此,在初次访问时,侦听器不会处理实际的访问者模式。实际的访问者模式和通过访问进行的处理是由表达式基侦听器类扩展的方法完成的。

侦听器识别表达式:

@Override public void enterListener(ExprParser.EXPR_CONTEXTContext ctx) { 
  //some code to view the compilation process
}
Run Code Online (Sandbox Code Playgroud)

表达式规则获取名称标签:

'EXPR_CONTEXT' expression             # EXPR_CONTEXT //the tree label
Run Code Online (Sandbox Code Playgroud)

表达式 Walker 的实现:

public class ExprWalker extends ExprBaseListener {

  @Override 
  public void enterListener(ExprParser.EXPR_CONTEXTContext ctx) { 

    java.util.List<ExprParser.ExpressionContext> e = ctx.expression();

    System.out.println("EXPRESSION: " //print action
    + e.get(0).getText() + ", " //first element
    + e.get(1).getText() //second element
    + ", " + ... + ", " //number of elements
    + e.get(N).getText()); //last element

}
Run Code Online (Sandbox Code Playgroud)

然后主文件与walker一起行走:

ParseTree tree = parser.parse(); //parse the tree
Run Code Online (Sandbox Code Playgroud)

间奏曲:在应用步行者访问模式之前,人们可以想象树段优化或处理模式。解析树可以在这里作为源代码树的单独归纳来处理。这种方法支持更复杂的代码和树处理模式。

ParseTreeWalker walker = new ParseTreeWalker(); //get the walker
walker.walk(new ExprWalker(), tree); //start visiting
Run Code Online (Sandbox Code Playgroud)

  • 无需创建“new ParseTreeWalker()”。只需使用“ParseTreeWalker.DEFAULT.walk(...)”。 (2认同)