antlr + python

想对 C 语言的源代码做变换,变换逻辑不太复杂,用 C++/LLVM 实现不合算,尝试一下 antlr4 + python。

访问 ANTLR 官网
下载 antlr 语法文件 .g4 的编译器等工具
pip install antlr4-tools

安装 Java 11 和 ANTLR jar
antlr4

antlr4

虽然 C 源代码结构清晰,但操作系统将其保存为字符串(文件),进入程序的处理流程时也是作为字符串类型的变量。另一方面,拿 C 语言来说,方便程序处理的 “结构体” 数据结构在内存中的格式是 “含有指针的若干记录” 的结构体格式,与字符串有差异。把源代码从字符串格式变换成结构体格式的程序叫语法分析器(parser)。

antlr4 可以根据语法生成识别该语法的 parser,以 Java,Python 或 C++ 等语言的源码形式给出的。不同语法的 parser 存在公共的部分,这部分作项目依赖的 runtime。对于某个语法(用 .g4 文件描述), parser 中特殊的部分合入项目的源码。

源码经 parser 解析后变为语法树,

python

安装 Python runtime
pip install antlr4-python3-runtime

准备好 github 上的 C 语法描述文件,生成 Parser 和 Visitor 如下
antlr4 -Dlanguage=Python3 -visitor -no-listener C.g4

生成如下文件:
C.g4 C.interp CLexer.interp CLexer.py CLexer.tokens CParser.py C.tokens CVisitor.py

API

在 python 中使用 parser 的代码:

import sys
from antlr4 import *
from CLexer import CLexer
from CParser import CParser
from CVisitor import CVisitor

class FuseRewriter(CVisitor):

    def visitTerminal(self, node):
        if node.symbol.type == Token.EOF:
            return self.defaultResult()
        else:
            return [node.getText()]
    
    def visitErrorNode(self, node):
        print('parse error')
        assert False

    def defaultResult(self):
        return []
    
    def aggregateResult(self, aggregate, nextResult):
        return aggregate + nextResult
    
    def visitPostfixExpression(self, ctx: CParser.PostfixExpressionContext):
        return super().visitPostfixExpression(ctx)
    

def main():
    if len(sys.argv) > 1:
        input_stream = FileStream(sys.argv[1])
    else:
        input_stream = InputStream(sys.stdin.read())
    
    lexer = CLexer(input_stream)
    stream = CommonTokenStream(lexer)
    parser = CParser(stream)
    tree = parser.compilationUnit()

    c_tree_str = tree.toStringTree(recog=parser)
    # print(c_tree_str)

    visitor = FuseRewriter()
    return visitor.visit(tree)

if __name__ == '__main__':
    tokens = main()
    print(' '.join(tokens))

visitor

antlr4 根据语法描述文件(.g4)生成语法分析器,该分析器可以根据源文件构建语法树(parse tree)。语法树是一种树形数据结构,其根节点代表整个源文件,叶子节点(称为 TerminalNode,终结符节点)代表不可再分的词法单元(如一个整数常量),其余的位于树中间部分的节点代表语法规则节点(RuleNode)。如果源文件中存在语法错误,则其对应的语法树中还会出现错误节点(ErrorNode)。

操作树形数据结构的算法,可以采用观察者设计模式(Listener)或访问者设计模式(Visitor),antlr4 runtime 通过提供观察者和访问者的基类(Base class),支持了上述两种设计模式。

你可能感兴趣的:(编译原理,python,开发语言,antlr)