Antlr4 - 有没有使用ParseTree Walker的简单示例?

18

Antlr4有一个新的类ParseTreeWalker。但是我该怎么使用它?我正在寻找一个最小的工作示例。我的语法文件是'gram.g4',我想解析一个名为'program.txt'的文件。

到目前为止,这是我的代码。(假设ANTLR已经运行了我的语法文件,并创建了所有的gramBaseListenergramLexer等等):

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import static org.antlr.v4.runtime.CharStreams.fromFileName;

public class launch{
public static void main(String[] args) {

    CharStream cs = fromFileName("gram.g4");  //load the file
    gramLexer lexer = new gramLexer(cs);  //instantiate a lexer
    CommonTokenStream tokens = new CommonTokenStream(lexer); //scan stream for tokens
    gramParser parser = new gramParser(tokens);  //parse the tokens

    // Now what??  How do I connect the above with the below? 

    ParseTreeWalker walker = new ParseTreeWalker();  // how do I use this to parse program.txt??
}}

我正在使用Java,但我认为其他语言类似。

ANTLR文档 (http://www.antlr.org/api/Java/index.html) 缺少示例。虽然网络上有许多教程,但它们大多是针对ANTLR版本3的。使用版本4的教程很少能正常工作或者已经过时(例如,没有parser.init()功能,而像ANTLRInputStream这样的类已经过时)。

提前感谢任何能够提供帮助的人。


抱歉这么晚才回复您。能否提供您的简单语法文件?如果所有相关源代码都包含在内,答案可能会更加明显。 - Sean
2个回答

22

对于文法中的每个解析规则,生成的解析器将具有相应的方法名。调用该方法将从该规则开始解析。

因此,如果您的 "根规则" 命名为 start,则可以通过 gramParser.start() 开始解析,该方法返回一个 ParseTree。然后,可以将此树与您想要使用的监听器一起馈送到 ParseTreeWalker 中。

总的来说,它可能看起来像这样(已由 OP 进行编辑):

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import static org.antlr.v4.runtime.CharStreams.fromFileName;

public class launch{
public static void main(String[] args) {

    CharStream cs = fromFileName("program.txt");  //load the file
    gramLexer lexer = new gramLexer(cs);  //instantiate a lexer
    CommonTokenStream tokens = new CommonTokenStream(lexer); //scan stream for tokens
    gramParser parser = new gramParser(tokens);  //parse the tokens

    ParseTree tree = parser.start(); // parse the content and get the tree
    Mylistener listener = new Mylistener();

    ParseTreeWalker walker = new ParseTreeWalker();
    walker.walk(listener,tree);
}}

************ 新文件 Mylistener.java ************

public class Mylistener extends gramBaseListener {
        @Override public void enterEveryRule(ParserRuleContext ctx) {  //see gramBaseListener for allowed functions
            System.out.println("rule entered: " + ctx.getText());      //code that executes per rule
        }
    }

当然,您必须用您的BaseListener实现替换<listener>
另外一个小提示:在Java中,惯例是以大写字母开头命名类,我建议您坚持这种方式,以使代码更易于其他人阅读。

错误:java:找不到符号 符号:方法start() 位置:类型为gramParser的变量解析器 - john k
3
好的,start()是我在gram.g4中规则的实际名称。抱歉,我对这一切还很陌生。有几个问题。所以listener是由gramBaseListener gbl = new gramBaseListener()创建的。现在我该如何让它解析一个程序? - john k
是的,完全正确。我只是用start()作为例子,因为我不知道你的语法规则的名称。Baselistener什么也不做。你必须扩展它并覆盖你想要执行一些代码的相应方法。 - Raven
文件正在被粘贴,此时您调用 start()。正如上面所述:为了从中看到任何内容,您必须覆盖监听器中的一些方法。另一个好方法是使用随 ANTLR 一起提供的 TestRig 以图形方式显示解析树。您可以在网络上搜索它... - Raven
啊,好的,谢谢。但是我在任何地方阅读的都说ANTLR会自动为您创建一个监听器。你是说它会创建一个空监听器吗?因为那有点愚蠢。将该逻辑扩展到解析树、标记、解析文件等等。 - john k
显示剩余3条评论

4

这个示例应该可以在ANTLR 4.8版本下工作。

在下面的示例中,您可以找到设置Java环境、API和监听器的参考资料。

public class Launch {
    public static void main(String[] args) {
        InputStream inputStream = null;
        MyprogramLexer programLexer = null;
        try {
            File file = new File("/program.txt");
            inputStream = new FileInputStream(file);
            programLexer = new MyprogramLexer(CharStreams.fromStream(inputStream)); // read your program input and create lexer instance
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
        
        /* assuming a basic grammar:
            myProgramStart: TOKEN1 otherRule TOKEN2 ';' | TOKENX finalRule ';'
            ...
        */
        CommonTokenStream tokens = new CommonTokenStream(programLexer); // get tokens
        MyParser parser = new MyParser(tokens);
        MyProgramListener listener = new MyProgramListener();   // your custom extension from BaseListener
        parser.addParseListener(listener);
        parser.myProgramStart().enterRule(listener);    // myProgramStart is your grammar rule to parse

        // what we had built?
        MyProgram myProgramInstance = listener.getMyProgram();    // in your listener implementation populate a MyProgram instance
        System.out.println(myProgramInstance.toString());
    }
}

References:


使用这种技术与使用ParseTreeWalker相比有什么好处吗?当我在我的TypeScriptParserBaseListener子类中使用这种技术时,所有的监听器方法中ctx.getText()总是返回一个空字符串,不知道为什么。 - abulka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接