python antlr_使用ANTLR在5分钟内用Java解析任何语言:例如Python
python antlr
我喜歡出于多種目的處理代碼,例如靜態(tài)分析或自動(dòng)重構(gòu)。 對(duì)我來說,有趣的部分是推理從抽象語法樹(AST)構(gòu)建的模型。 為此,您需要一種從源文件中獲取AST的方法。 可以使用ANTLR輕松完成此操作,并在此處提供完整語法的集合: https : //github.com/antlr/grammars-v4
謝謝大家的語法!
我們將只為Python 3編寫一個(gè)腳本,對(duì)于Python 2來說也應(yīng)該可以正常工作。如果我們需要做一些小的調(diào)整,我們可以從這個(gè)基礎(chǔ)上輕松地做到這一點(diǎn)。
獲得語法
首先,我們要學(xué)習(xí)語法。
只需訪問https://github.com/antlr/grammars-v4并獲取所需的語法即可。 大多數(shù)語法都有非常寬松的許可。
R,Scala,Python,Swift,PHP等許多語言都有數(shù)十種語法。 Java也有一個(gè),但是對(duì)于Java,您更喜歡使用JavaParser,對(duì)嗎?
只需將語法復(fù)制到src / main / antlr下的新項(xiàng)目中
使用Gradle設(shè)置項(xiàng)目
現(xiàn)在,我們將使用Gradle設(shè)置構(gòu)建腳本。
我們將使用ANTLR4插件從melix ,因?yàn)槲矣X得它更靈活的中描述的的官方文件 。
我們將在特定的程序包( me.tomassetti.pythonast.parser )中生成代碼,因此將在從該程序包派生的目錄(build / generate-src / me / tomassetti / pythonast / parser)中生成代碼。
buildscript {repositories {maven {name 'JFrog OSS snapshot repo'url 'https://oss.jfrog.org/oss-snapshot-local/'}jcenter()}dependencies {classpath 'me.champeau.gradle:antlr4-gradle-plugin:0.1.1-SNAPSHOT'} }repositories {mavenCentral()jcenter() }apply plugin: 'java' apply plugin: 'me.champeau.gradle.antlr4'antlr4 {source = file('src/main/antlr')output = file('build/generated-src/me/tomassetti/pythonast/parser')extraArgs = ['-package', 'me.tomassetti.pythonast.parser'] }compileJava.dependsOn antlr4sourceSets.main.java.srcDirs += antlr4.outputconfigurations {compile.extendsFrom antlr4 }task fatJar(type: Jar) {manifest {attributes 'Implementation-Title': 'Python-Parser','Implementation-Version': '0.0.1'}baseName = project.name + '-all'from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }with jar }我還添加了fatJar任務(wù)。 該任務(wù)將產(chǎn)生一個(gè)包含所有依賴項(xiàng)的JAR。 我使用它可以更輕松地將解析器導(dǎo)入Jetbrains MPS。
要從語法生成解析器,您只需運(yùn)行g(shù)radle antlr4。
然后,您必須向您的IDE解釋它應(yīng)該考慮build / Generated-src下的代碼。
如何調(diào)用解析器
現(xiàn)在讓我們看看如何調(diào)用解析器。
public class ParserFacade {private static String readFile(File file, Charset encoding) throws IOException {byte[] encoded = Files.readAllBytes(file.toPath());return new String(encoded, encoding);}public Python3Parser.File_inputContext parse(File file) throws IOException {String code = readFile(file, Charset.forName("UTF-8"));Python3Lexer lexer = new Python3Lexer(new ANTLRInputStream(code));CommonTokenStream tokens = new CommonTokenStream(lexer);Python3Parser parser = new Python3Parser(tokens);return parser.file_input();} }我們的ParserFacade只有一個(gè)名為parse的公共方法。 它獲取一個(gè)文件并返回AST。 沒有比這更簡(jiǎn)單的了。
讓我們看一些AST
讓我們看一個(gè)簡(jiǎn)單的文件:
def sum(a, b):return a + bprint("The sum of %i and %i is %i" % (5, 3, sum(5, 3)))現(xiàn)在獲取AST。 我們可以使用以下代碼進(jìn)行打印:
public class AstPrinter {public void print(RuleContext ctx) {explore(ctx, 0);}private void explore(RuleContext ctx, int indentation) {String ruleName = Python3Parser.ruleNames[ctx.getRuleIndex()];for (int i=0;i<indentation;i++) {System.out.print(" ");}System.out.println(ruleName);for (int i=0;i<ctx.getChildCount();i++) {ParseTree element = ctx.getChild(i);if (element instanceof RuleContext) {explore((RuleContext)element, indentation + 1);}}}}如果我們解析簡(jiǎn)單的示例并使用AstPrinter進(jìn)行打印,我們將獲得一個(gè)超級(jí)復(fù)雜的AST。 第一行看起來像:
file_inputstmtcompound_stmtfuncdefparameterstypedargslisttfpdeftfpdefsuitestmtsimple_stmtsmall_stmtflow_stmtreturn_stmttestlist...對(duì)于解析器的構(gòu)建方式,有很多無效的規(guī)則。 在解析時(shí)這很有意義,但是會(huì)產(chǎn)生非常污染的AST。 我認(rèn)為有兩種不同的ASTS:一種易于生成的解析AST ,另一種易于推理的邏輯AST 。 幸運(yùn)的是,我們可以毫不費(fèi)力地將第一個(gè)轉(zhuǎn)換為后者。
一種簡(jiǎn)單的方法是列出僅包裝程序的所有規(guī)則,然后跳過它們,取而代之的是唯一的子規(guī)則。 我們可能必須對(duì)此進(jìn)行優(yōu)化,但是作為第一步的近似,我們只是跳過只有一個(gè)子節(jié)點(diǎn)的節(jié)點(diǎn),這是另一個(gè)解析器規(guī)則(無終端)。
這樣,我們從164個(gè)節(jié)點(diǎn)增加到28個(gè)節(jié)點(diǎn)。結(jié)果邏輯AST為:
file_inputfuncdefparameterstypedargslisttfpdeftfpdefsuitesimple_stmtreturn_stmtarith_expratomatomsimple_stmtpoweratomtrailertermstringatomtestlist_compintegerintegerpoweratomtrailerarglistintegerinteger在這棵樹中,我們應(yīng)該將所有內(nèi)容映射到我們理解的概念,而無需人工節(jié)點(diǎn),只是出于解析原因而創(chuàng)建的節(jié)點(diǎn)。
結(jié)論
編寫解析器并不是我們可以產(chǎn)生最大價(jià)值的地方。 我們可以輕松地重用現(xiàn)有語法,生成解析器并使用這些解析器構(gòu)建我們的智能應(yīng)用程序。
那里有幾個(gè)解析器生成器,其中大多數(shù)足以滿足您可以實(shí)現(xiàn)的大多數(shù)目標(biāo)。 在它們當(dāng)中,我傾向于比其他人更多地使用ANTLR:它很成熟,得到支持,速度很快。 它產(chǎn)生的AST可以使用異構(gòu)API(我們?yōu)槊糠N類型的節(jié)點(diǎn)生成單個(gè)類)和同類API(我們可以詢問每個(gè)節(jié)點(diǎn)代表哪個(gè)規(guī)則及其子列表)進(jìn)行導(dǎo)航。
ANTLR的另一個(gè)巨大好處是存在隨時(shí)可以使用的語法。 構(gòu)建語法需要經(jīng)驗(yàn)和一些工作。 特別是對(duì)于Java或Python這樣的復(fù)雜GPL。 它還需要非常廣泛的測(cè)試。 即使我們已經(jīng)使用JavaParser解析了成千上萬個(gè)文件,我們?nèi)匀话l(fā)現(xiàn)JavaParser背后的Java 8語法存在一些小問題。 如果可以避免的話,這是現(xiàn)在編寫自己的語法的一個(gè)很好的理由。
- 順便說一下,所有代碼都可以在github上找到: python-ast
翻譯自: https://www.javacodegeeks.com/2016/02/parsing-language-java-5-minutes-using-antlr-example-python.html
python antlr
總結(jié)
以上是生活随笔為你收集整理的python antlr_使用ANTLR在5分钟内用Java解析任何语言:例如Python的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 备案说要扣车什么意思(备案说要扣车)
- 下一篇: Java与Python:哪一个最适合您?