将Antlr4的语法文件生成辅助代码,有两种方法,一种是通过IDEA插件,一种是用运行jar包生成。
Antlr的遍历器有监听器(lister)与访问器(visiter)。在实际过程中,发现访问器更加好用,所以以下是采用访问器的例子。
1、一个简单的例子
以《Antlr4》书中第一章为例,记录基本的开发流程。
g4文件:
grammarHello;// Define a grammar called Helloparse:prog+;// match keyword hello followed by an identifierprog:'hello'ID;ID:[a-z]+;// match lower-case identifiersWS:[\t\r\n]+->skip;// skip spaces, tabs, newlines, \r (Windows)
该语法文件很简单,目的是识别hello开头的短句。生成解析器的方法如下:
1.1 利用IDEA ANTLR4插件
右键g4语法文件,选择Configure ANTLR...,配置输出路径和目标语言,勾选generate parse tree visitor。再右键g4语法文件,点击Generate ANTLR Recognizer生成代码。
1.2 直接命令行运行
在项目中引进antlr-4.7.2-complete.jar包,
java -jar antlr-4.7.2-complete.jar src/01-Hello/Hello.g4 -Dlanguage=Python3 -visitor -o src/01-Hello/py-parser -Xexact-output-dir
配置一个application,以运行org.antlr.v4.Tool类。
2、编写业务代码
针对这个例子,可以假设做个实现目标,如提取出下面文件中每一行以hello开头的第二个词。
test.txt
hello world
hello python
hello cpp
hello java
业务代码:
import sys
from antlr4 import *
from a1_Hello.py_parser.HelloLexer import HelloLexer
from a1_Hello.py_parser.HelloParser import HelloParser
from a1_Hello.py_parser.HelloVisitor import HelloVisitor
def main(argv):
input = FileStream(argv[1])
lexer = HelloLexer(input)
stream = CommonTokenStream(lexer)
parser = HelloParser(stream)
tree = parser.parse()
# print(tree.toStringTree(recog=parser))
visitor = NewHelloVisitor()
x = visitor.visit(tree)
print(x)
class NewHelloVisitor(HelloVisitor):
```重写visitor```
def __init__(self):
super().__init__()
def visitParse(self, ctx: HelloParser.ParseContext):
second_words = []
for x in ctx.getChildren():
second_words.append(self.visit(x))
return second_words
def visitProg(self, ctx: HelloParser.ProgContext):
return ctx.ID().getText()
if __name__ == '__main__':
main(sys.argv)
结果:
['world', 'python', 'cpp', 'java']
补一个目标代码为Java的实现代码:
package hello;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import hello.parse.HelloLexer;
import hello.parse.HelloParser;
import hello.parse.HelloBaseVisitor;
import java.util.ArrayList;
public class TestHello {
public static void main(String[] args) {
CharStream input = CharStreams.fromString("hello world\rhello java\rhello python");
HelloLexer lexer = new HelloLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
HelloParser parser = new HelloParser(tokens);
ParseTree tree = parser.parse(); // parse HelloVisitorTest vt = new HelloVisitorTest();
vt.visit(tree);
for (String s : vt.arrayList) {
System.out.println(s);
}
}
}
class HelloVisitorTest extends HelloBaseVisitor {
ArrayList arrayList = new ArrayList<>();
@Override
public String visitProg(HelloParser.ProgContext ctx) {
arrayList.add(ctx.ID().getText());
return "";
}
@Override
public String visitParse(HelloParser.ParseContext ctx) {
return super.visitParse(ctx);
}
}
当然上面有很多不完善的地方,这里就不再细说。
3、建议
3.1
我以前做过的项目,曾用Antlr4来做语言转换,比如把简单的SAS语言转成python来运行。这样的浅层应用是最合适的。但如果真要做成一个完整的语言编程器,只用Antlr4还太单薄。可以在语法解析前端采用antlr,中间构建语法抽象层,后台用LLVM优化。
3.2
在使用Antlr4解析语法时,有时为了处理一些特殊的语法,会采用一些在语法文件嵌入代码等高级的处理方法。虽然能解决问题,但代码耦合的厉害。所以不太建议用这种方法。当然也是因为自己不喜欢太复杂的东西。
【参考】By Developers, For Developerspragprog.comantlr/antlr4github.comjszheng/py3antlr4bookgithub.com