使用ANTLR在5分钟内用Java解析任何语言:例如Python

我喜欢出于多种目的处理代码,例如静态分析或自动重构。 对我来说,有趣的部分是推理从抽象语法树(AST)构建的模型。 为此,您需要一种从源文件中获取AST的方法。 使用ANTLR和完整的语法集合可在此处轻松完成: https : //github.com/antlr/grammars-v4

谢谢大家的语法!

谢谢大家的语法!

我们将只为Python 3选择一个,而在Python 2上也应该可以正常工作。如果我们需要做一些小的调整,我们可以从这个基础上轻松地做到这一点。

获得语法

首先,我们要学习语法。

只需访问https://github.com/antlr/grammars-v4并获取所需的语法即可。 大多数语法都有非常宽松的许可。

R,Scala,Python,Swift,PHP等许多语言都有数十种语法。 Java也有一个,但是对于Java,您更喜欢使用JavaParser,对吗?

只需将语法复制到src / main / antlr下的新项目中

使用Gradle设置项目

现在,我们将使用Gradle设置构建脚本。

我们将使用ANTLR4插件从melix ,因为我觉得它更灵活的中描述的的官方文件 。

我们将在特定的程序包( 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任务。 该任务将产生一个包含所有依赖项的JAR。 我使用它可以更轻松地将解析器导入Jetbrains MPS。

要从语法生成解析器,您只需运行gradle antlr4。

然后,您必须向您的IDE解释它应该考虑build / Generated-src下的代码

如何调用解析器

现在让我们看看如何调用解析器。

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只有一个名为parse的公共方法。 它获取一个文件并返回AST。 没有比这更简单的了。

让我们看一些AST

让我们看一个简单的文件:

def sum(a, b):return a + bprint("The sum of %i and %i is %i" % (5, 3, sum(5, 3)))

现在获取AST。 我们可以使用以下代码进行打印:

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);}}}}

如果我们解析简单的示例并使用AstPrinter进行打印,我们将获得一个超级复杂的AST。 第一行看起来像:

file_inputstmtcompound_stmtfuncdefparameterstypedargslisttfpdeftfpdefsuitestmtsimple_stmtsmall_stmtflow_stmtreturn_stmttestlist...

对于解析器的构建方式,有很多废止的规则。 在解析时这很有意义,但是会产生非常污染的AST。 我认为有两种不同的ASTS:一种易于生成的解析AST ,另一种易于推理的逻辑AST 。 幸运的是,我们可以在不花太多精力的情况下改造第一个。

一种简单的方法是列出仅包装程序的所有规则,然后跳过它们,取而代之的是唯一的子规则。 我们可能必须对此进行优化,但作为第一步的近似,我们只是跳过只有一个子节点的节点,这是另一个解析器规则(无终端)。

这样,我们从164个节点增加到28个。结果逻辑AST为:

file_inputfuncdefparameterstypedargslisttfpdeftfpdefsuitesimple_stmtreturn_stmtarith_expratomatomsimple_stmtpoweratomtrailertermstringatomtestlist_compintegerintegerpoweratomtrailerarglistintegerinteger

在这棵树中,我们应该将所有内容映射到我们理解的概念,而无需人工节点,只是出于解析原因而创建的节点。

结论

编写解析器并不是我们可以产生最大价值的地方。 我们可以轻松地重用现有语法,生成解析器并使用这些解析器构建我们的智能应用程序。

那里有几个解析器生成器,其中大多数足以满足您可以实现的大多数目标。 在它们当中,我倾向于比其他人更多地使用ANTLR:它已经成熟,被支持并且速度很快。 它产生的AST可以使用异构API(我们为每种类型的节点生成单个类)和同类API(我们可以询问每个节点代表哪个规则及其子代列表)进行导航。

ANTLR的另一个巨大好处是存在随时可以使用的语法。 构建语法需要经验和一些工作。 特别是对于Java或Python这样的复杂GPL。 它还需要非常广泛的测试。 即使我们已经使用JavaParser解析了成千上万的文件,我们仍然发现JavaParser背后的Java 8语法存在一些小问题。 如果可以避免的话,这是现在编写自己的语法的一个很好的理由。

  • 顺便说一下,所有代码都可以在github上找到: python-ast

翻译自: https://www.javacodegeeks.com/2016/02/parsing-language-java-5-minutes-using-antlr-example-python.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/354418.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[转]Global exception handling in Web API 2.1 and NLog

本文转自&#xff1a;https://stackoverflow.com/questions/25865610/global-exception-handling-in-web-api-2-1-and-nlog In Web API 2.1 is new Global Error Handling. I found some example how to log exceptions into Elmah ( elmah sample ). But I use NLog to log er…

linux服务器配置ssl证书

一、申请ssl证书 这里有申请域名的ssl证书的步骤&#xff1a; https://blog.csdn.net/weixin_38797742/article/details/109471250 二、配置服务器 打开项目对应的配置文件&#xff0c;配置图中三行信息&#xff0c;端口、还有申请的证书的所在目录。 listen 443 ssl; ssl_…

将自定义的集合数据使用pagehelper进行分页

PageHelper 是一个用于在 MyBatis 中进行分页查询的插件&#xff0c;它主要设计用于对从数据库查询的结果进行分页。 如果数据不是直接从 MySQL 数据库中查询得到&#xff0c;而是已经存在于内存中的集合&#xff08;如 List、ArrayList 等&#xff09;&#xff0c;你可以通过以…

视图层的数据绑定

数据绑定WXML 中的动态数据均来自对应 Page 的 data。 只要把data中的数据改变&#xff0c;视图层就会自动改变&#xff0c;无需使用DOM操作。 简单绑定数据绑定使用 Mustache 语法&#xff08;双大括号&#xff09;将变量包起来 使用方法&#xff1a; {{ 内容 }} 在组件中使用…

使用dwebsocket在Django中使用Websocket

一 什么是Websocket WebSocket是一种在单个TCP连接上进行全双工通信的协议 WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就直接…

ejb 2.0 3.0_Java EE 6测试第I部分– EJB 3.1可嵌入API

ejb 2.0 3.0我们从Enterprise JavaBeans开发人员那里听到的最常见的请求之一就是需要改进的单元/集成测试支持。 EJB 3.1规范引入了EJB 3.1可嵌入API&#xff0c;用于在Java SE环境中执行EJB组件。 与传统的基于Java EE服务器的执行不同&#xff0c;可嵌入式用法允许客户端代码…

如何添加自动更新Play Framework 2.X项目的版本号

我想拥有要发布新版本时自动更新的版本号&#xff0c;因此我着手了解如何使用Play Framework进行此操作。 我发现我可以将其基于sbt-release插件&#xff0c;但是并不是那么简单。 这是我的策略&#xff0c;因此最后我要做的就是运行“ activator release ”&#xff1a; 1.添…

JS容易犯错的this和作用域

var someuser { name: byvoid, func: function() {console.log(this.name); }}; var foo { name: foobar};someuser.func(); // 输出 byvoidfoo.func someuser.func; foo.func(); // 输出 foobarname global;func someuser.func; func(); // 输出 global在 JavaScript 中,…

【原创】Linux安装Redis

【注】说是原创其实是从各个博客中copy出来的&#xff0c;不过经本人总结&#xff0c;也算是半个原创吧&#xff0c;哈哈... 1.安装环境&#xff1a; 2.安装步骤 2.1 安装目录 cd /usr/local mkdir redis 2.2 下载包 wget http://download.redis.io/releases/redis-3.2.11.tar…

前端:屏蔽F12审查元素,禁止修改页面代码

一、屏蔽F12 审查元素 document.onkeydown function(){if(window.event && window.event.keyCode 123) {alert("F12被禁用");event.keyCode0;event.returnValuefalse; } if(window.event && window.event.keyCode 13) {window.event.keyCode 50…

SDN第四次作业

ryu控制器 ryu架构清晰&#xff0c;支持OpenFlow全部版本&#xff0c;采用Python语言进行APP开发。其上层为OpenStack和Web提供了编程接口,中间为Ryu自行研发的应用组件,最下层为Ryu底层实现的基本组件。 onos控制器 onos是面向运营商网络的SDN操作系统&#xff0c;采用java语言…

resteasy_RESTEasy教程第2部分:Spring集成

resteasyRESTEasy提供了对Spring集成的支持&#xff0c;这使我们能够将Spring bean作为RESTful WebServices公开。 步骤&#xff03;1&#xff1a;使用Maven配置RESTEasy Spring依赖项。 <project xmlnshttp:maven.apache.orgPOM4.0.0 xmlns:xsihttp:www.w3.org2001XMLSche…

前端代码加密

虽然浏览器会把加密的代码破解在解析&#xff0c;但是还是可以稍微加密&#xff0c;或者人家就是破解不了呢哈哈哈哈&#xff01;&#xff01;&#xff01; 1、脚本之家 推荐&#xff1a;HTML原代码加、解密脚本 https://www.jb51.net/tools/html_jiami.htm CSS代码格式化和…

原型链继承

转载于:https://www.cnblogs.com/cmblog/p/8079773.html

清洁责任–摆脱均等,compareTo和toString

您是否看过Java中Object类的javadoc&#xff1f; 大概。 您倾向于时不时地到达那里&#xff0c;然后沿着继承树进行挖掘。 您可能已经注意到的一件事是&#xff0c;每个类都必须继承许多方法。 实现自己而不是坚持使用原始方法的最喜欢的方法可能是.toString&#xff08;&#…

密码保护

1.更新User对象&#xff0c;设置对内的_password class User(db.Model): __tablename__ user _password db.Column(db.String(200), nullableFalse) #内部使用 2.编写对外的password from werkzeug.security import generate_password_hash, check_password_hash property d…

linux 安装wdcp控制面板

**wdCP是WDlinux Control Panel的简称,**是一套通过WEB控制和管理服务器的Linux服务器管理系统以及虚拟主机管理系统。 可以查看服务器情况,资源利用率,系统负载,内存使用率,带宽使用率&#xff1b; 可以轻松创建网站,开站点,发布网站,创建FTP,创建mysql数据库&#xff1b; 可以…

9.proc目录下的文件和目录详解

1./proc目录下的文件和目录详解 /proc:虚拟目录.是内存的映射,内核和进程的虚拟文件系统目录,每个进程会生成1个pid&#xff0c;而每个进程都有1个目录. /proc/Version:内核版本 /proc/sys/kernel:系统内核功能 /proc/sys/net/ipv4: /proc/meminfo:系统内存信息,free -m /proc/…

java线程死锁_Java线程死锁–案例研究

java线程死锁本文将描述从在IBM JVM 1.6上运行的Weblogic 11g生产系统中观察到的最新Java死锁问题的完整根本原因分析。 此案例研究还将证明掌握线程转储分析技能的重要性&#xff1b; 包括用于IBM JVM Thread Dump格式。 环境规格 – Java EE服务器&#xff1a;Oracle Weblo…

JS实现禁止浏览器后退返回上一页

<script type"text/javascript"> $(function() {//防止页面后退history.pushState(null, null, document.URL);window.addEventListener(popstate, function () {history.pushState(null, null, document.URL);});}) </script>