使用ANTLR和Java创建外部DSL

在以前的一段时间里,我曾写过有关使用Java的内部DSL的文章。 在Martin Fowler撰写的《 领域特定语言 》一书中,他讨论了另一种称为外部DSL的DSL,其中DSL是用另一种语言编写的,然后由宿主语言进行解析以填充语义模型。

在前面的示例中,我讨论了有关创建用于定义图形的DSL的问题。 使用外部dsl的优点是,图形数据中的任何更改都不需要重新编译程序,而是程序可以仅加载外部dsl,创建解析树然后填充语义模型。 语义模型将保持不变,并且使用语义模型的优点是无需更改语义模型即可对DSL进行修改。 在内部DSL和外部DSL之间的示例中,我没有修改语义模型。 为了创建外部DSL,我使用了ANTLR 。

什么是ANTLR?

官方网站上给出的定义是:

ANTLR(另一种语言识别工具)是功能强大的解析器生成器,用于读取,处理,执行或翻译结构化文本或二进制文件。 它被广泛用于构建语言,工具和框架。 ANTLR通过语法生成可以构建和遍历语法树的语法分析器。

根据以上定义,ANTLR的显着特征是:

  • 用于结构化文本或二进制文件的解析器生成器
  • 可以建造和行走解析树

语义模型

在此示例中,我将利用ANTLR的上述功能来解析DSL,然后遍历解析树以填充语义模型。 概括地说,语义模型由GraphEdgeVertex类组成,它们分别表示Graph和Graph的Edge和Vertex。 下面的代码显示了类定义:

public class Graph {private List<Edge> edges;private Set<Vertex> vertices;public Graph() {edges = new ArrayList<>();vertices = new TreeSet<>();}public void addEdge(Edge edge){getEdges().add(edge);getVertices().add(edge.getFromVertex());getVertices().add(edge.getToVertex());}public void addVertice(Vertex v){getVertices().add(v);}public List<Edge> getEdges() {return edges;}public Set<Vertex> getVertices() {return vertices;}public static void printGraph(Graph g){System.out.println("Vertices...");for (Vertex v : g.getVertices()) {System.out.print(v.getLabel() + " ");}System.out.println("");System.out.println("Edges...");for (Edge e : g.getEdges()) {System.out.println(e);}}}public class Edge {private Vertex fromVertex;private Vertex toVertex;private Double weight;public Edge() {}public Edge(Vertex fromVertex, Vertex toVertex, Double weight) {this.fromVertex = fromVertex;this.toVertex = toVertex;this.weight = weight;}@Overridepublic String toString() {return fromVertex.getLabel() + " to " + toVertex.getLabel() + " with weight " + getWeight();}public Vertex getFromVertex() {return fromVertex;}public void setFromVertex(Vertex fromVertex) {this.fromVertex = fromVertex;}public Vertex getToVertex() {return toVertex;}public void setToVertex(Vertex toVertex) {this.toVertex = toVertex;}public Double getWeight() {return weight;}public void setWeight(Double weight) {this.weight = weight;}
}public class Vertex implements Comparable<Vertex> {private String label;public Vertex(String label) {this.label = label.toUpperCase();}@Overridepublic int compareTo(Vertex o) {return (this.getLabel().compareTo(o.getLabel()));}public String getLabel() {return label;}public void setLabel(String label) {this.label = label;}
}

创建DSL

在创建语法规则之前,让我们先提出语言的结构。 我打算提出的结构是这样的:

Graph {A -> B (10)B -> C (20)D -> E (30)
}

Graph块中的每条线代表一条边,该边所涉及的顶点以及大括号中的值代表该边的权重。 我要强制执行的一个限制是,图不能具有悬空的顶点,即不属于任何边线的顶点。 可以通过稍微改变语法来消除此限制,但是我将其作为练习留给本文的读者。

创建DSL的首要任务是定义语法规则。 这些是您的词法分析器和解析器用来将DSL转换为抽象语法树 / 解析树的规则 。

然后,ANTLR利用此语法生成解析器,Lexer和侦听器,它们不过是Java类,用于扩展/实现ANTLR库中的某些类。 DSL的创建者必须利用这些Java类来加载外部DSL,对其进行解析,然后在解析器遇到某些节点时使用侦听器填充语义模型(将其视为XML的SAX解析器的变体)。

现在,我们已经非常简短地了解了ANTLR可以做什么以及使用ANTLR的步骤,我们将必须设置ANTLR,即下载ANTLR API jar并设置一些脚本来生成解析器和词法分析器,然后通过命令行尝试该语言。工具。 对于请访问这个从ANTLR官方教程,显示了如何设置ANTLR和一个简单的Hello World例子。

DSL语法

现在您已经设置了ANTLR,让我深入了解DSL的语法:

grammar Graph;
graph: 'Graph {' edge+ '}';
vertex: ID;
edge: vertex '->' vertex '(' NUM ')' ;
ID: [a-zA-Z]+;
NUM: [0-9]+;
WS: [ \t\r\n]+ -> skip;

让我们通过以下规则:

graph: 'Graph {' edge+ '}';

上面的语法规则(即开始规则)说,该语言应以“ Graph {”开头,以“}”结尾,并且必须至少包含一个边或多个边。

vertex: ID;
edge: vertex '->' vertex '(' NUM ')' ;
ID: [a-zA-Z]+;
NUM: [0-9]+;

以上四个规则说一个顶点至少应具有一个字符或多个字符。 边定义为两个顶点的集合,两个顶点之间用“->”分隔,并且在“()”中包含一些数字。

我将语法语言命名为“ Graph”,因此一旦使用ANTLR生成Java类(即解析器和词法分析器),我们最终将看到以下类:GraphParser,GraphLexer,GraphListener和GraphBaseListener。 前两个类处理解析树的生成,后两个类处理解析树的遍历。 GraphListener是一个接口,其中包含用于处理解析树的所有方法,即处理事件(例如,输入规则,退出规则,访问终端节点),此外,还包含用于处理与输入图相关的事件的方法规则,输入边缘规则并输入顶点规则。 我们将利用这些方法来拦截dsl中存在的数据,然后填充语义模型。

填充语义模型

我在资源包中创建了一个文件graph.gr,其中包含用于填充图形的DSL。 由于资源包中的文件在运行时可供ClassLoader使用,因此我们可以使用ClassLoader读取DSL脚本,然后将其传递给Lexer和解析器类。 使用的DSL脚本是:

Graph {A -> B (10)B -> C (20)D -> E (30)A -> E (12)B -> D (8)
}

以及加载DSL并填充语义模型的代码:

//Please resolve the imports for the classes used.
public class GraphDslAntlrSample {public static void main(String[] args) throws IOException {//Reading the DSL scriptInputStream is = ClassLoader.getSystemResourceAsStream("resources/graph.gr");//Loading the DSL script into the ANTLR stream.CharStream cs = new ANTLRInputStream(is);//Passing the input to the lexer to create tokensGraphLexer lexer = new GraphLexer(cs);CommonTokenStream tokens = new CommonTokenStream(lexer);//Passing the tokens to the parser to create the parse trea. GraphParser parser = new GraphParser(tokens);//Semantic model to be populatedGraph g = new Graph();//Adding the listener to facilitate walking through parse tree. parser.addParseListener(new MyGraphBaseListener(g));//invoking the parser. parser.graph();Graph.printGraph(g);}
}/*** Listener used for walking through the parse tree.*/
class MyGraphBaseListener extends GraphBaseListener {Graph g;public MyGraphBaseListener(Graph g) {this.g = g;}@Overridepublic void exitEdge(GraphParser.EdgeContext ctx) {//Once the edge rule is exited the data required for the edge i.e //vertices and the weight would be available in the EdgeContext//and the same can be used to populate the semantic modelVertex fromVertex = new Vertex(ctx.vertex(0).ID().getText());Vertex toVertex = new Vertex(ctx.vertex(1).ID().getText());double weight = Double.parseDouble(ctx.NUM().getText());Edge e = new Edge(fromVertex, toVertex, weight);g.addEdge(e);}
}

执行上述操作时的输出为:

Vertices...
A B C D E 
Edges...
A to B with weight 10.0
B to C with weight 20.0
D to E with weight 30.0
A to E with weight 12.0
B to D with weight 8.0

总而言之,本文创建了一个外部DSL,用于通过使用ANTLR填充图形数据。 我将增强这种简单的DSL,并将其公开为实用程序,供从事图形工作的程序员使用。

这篇文章非常讲究概念和代码,您可以随意提出任何疑问,以便我也可以尝试解决这些问题,以使他人受益。

参考:来自Experiences Unlimited博客的JCG合作伙伴 Mohamed Sanaulla 使用ANTLR和Java创建外部DSL 。

翻译自: https://www.javacodegeeks.com/2013/07/creating-external-dsls-using-antlr-and-java.html

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

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

相关文章

vue跨域解决及打包

打包之前需要修改如下配置文件&#xff1a; 配置文件一&#xff1a;build>>>utils.js (修改publicPath:"../../" , 这样写是处理打包后找不到静态文件&#xff08;图片路径失效&#xff09;的问题) 配置文件二&#xff1a;config>>>index.js(修改a…

8. Oracle 联机重做日志文件(ONLINE LOG FILE)

转载自&#xff1a;http://blog.csdn.net/leshami/article/details/5749556 一、Oracle中的几类日志文件 Redo log files -->联机重做日志 Archive log files -->归档日志 Alert log files -->告警日志 Trace files -->跟踪日志 user_dump_…

Bootstrap中实现图片圆角效果

Bootstrap 对图片的支持。Bootstrap 提供了三个可对图片应用简单样式的 class&#xff1a; .img-rounded&#xff1a;添加 border-radius:6px 来获得图片圆角。.img-circle&#xff1a;添加 border-radius:500px 来让整个图片变成圆形。.img-thumbnail&#xff1a;添加一些内边…

java 唯一索引冲突_JPA merge联合唯一索引无效问题解决方案

问题JPA的merge()操作 是合并的意思&#xff0c;就是当保存的实体时&#xff0c;根据主键id划分&#xff0c;如果已存在&#xff0c;那么就是更新操作&#xff0c;如果不存在&#xff0c;就是新增操作但是这个仅针对 主键id 划分&#xff0c;对联合唯一索引 无效&#xff0c;两…

Spring MVC测试框架入门–第1部分

最新推出的主要Spring框架是Spring MVC测试框架&#xff0c;Spring Guys声称它是“一流的JUnit支持&#xff0c;可通过流畅的API测试客户端和服务器端Spring MVC代码” 1 。 在这个博客以及下一个博客中&#xff0c;我将看一看Spring的MVC测试框架&#xff0c;并将其应用于我现…

metaclass

用metaclass来指定类C的元类是MyTypeclass MyType:def __init__(cls, *args, **kwargs):print(here!)#由于metaclassMyType&#xff0c;所以执行到这一步的时候&#xff0c;会调用MyType的构造函数 class C(metaclassMyType):def __init__(self):pass对象后面跟()&#xff0c;是…

Bootstrap中的条纹进度条使用案例

创建一个条纹的进度条的步骤如下&#xff1a;1.添加一个带有 class .progress 和 .progress-striped 的 <div>2.接着在上面的 <div> 内&#xff0c;添加一个带有 class .progress-bar 和 class progress-bar-* 的空的 <div>。其中&#xff0c;* 可以是succes…

LM拟合算法

一、 Levenberg-Marquardt算法 &#xff08;1&#xff09;ya*e.^(-b*x)形式拟合 clear all % 计算函数f的雅克比矩阵&#xff0c;是解析式 syms a b y x real; fa*exp(-b*x); Jsymjacobian(f,[a b]); % 拟合用数据。参见《数学试验》&#xff0c;p190&#xff0c;例2 % data_1…

java的前生今世_HBaseGC的前生今世-身世篇

网易视频云是网易倾力打造的一款基于云计算的分布式多媒体处理集群和专业音视频技术&#xff0c;提供稳定流畅、低时延、高并发的视频直播、录制、存储、转码及点播等音视频的PAAS服务&#xff0c;在线教育、远程医疗、娱乐秀网易视频云是网易倾力打造的一款基于云计算的分布式…

CapeDwarf – Java EE上的Google App Engine

我有很多爱好。 从早期的Java EE规范一路走来&#xff0c;并通过Java EE 7进行了“云”之旅&#xff0c;我很好奇看到新宣布的CapeDwarf项目有哪些库存&#xff0c;可以在内部引入Google的平台即服务&#xff0c;提供“ Google App Engine ” 。 到目前为止的故事 我确实使用了…

windows 服务中托管asp.net core

在windows 服务中托管asp.net core SDK 2.1.300 官方示例 1、添加运行标识符xml <PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> <RuntimeIdentifier>win7-x64</RuntimeIdentifier> </PropertyGroup> 2、添加包引用 d…

Bootstrap插件之-按钮插件

按钮 button.js 按钮的功能很丰富。通过控制按钮的状态或创建一组按钮并形成一些新的组件&#xff0c;例如工具条。 跨浏览器兼容性 在页面多次加载之间&#xff0c;Firefox 仍然保持表单控件的状态&#xff08;禁用状态和选择状态&#xff09;。一个解决办法是设置autocomplet…

第二周读书笔记——《构建之法》

【对一些实例的看法】 “我写了二叉树的遍历算法实现&#xff0c;在这里&#xff0c;二叉树是数据结构&#xff0c;遍历的实现细节是算法。C程序就是结果。但是这个程序有什么实际用处呢&#xff1f;在Java和其他一些语言中&#xff0c;似乎没有指针&#xff0c;那我可以不必了…

java springmvc 数据库事务_事务的简单回顾_JavaEE框架(Maven+SpringMvc+Spring+MyBatis)全程实战教程_Java视频-51CTO学院...

SpringMVCSpring MVC属于SpringFrameWork的后续产品&#xff0c;已经融合在Spring Web Flow里面。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色&#xff0c;这种分离让它们更容易进行定制。SpringSpring是一个开源框架&#xff0c;Spring是于2003 年兴起的…

异步重试模式

当您有一段经常失败且必须重试的代码时&#xff0c;此Java 7/8库提供了丰富且不引人注目的API&#xff0c;并提供了针对此问题的快速且可扩展的解决方案&#xff1a; ScheduledExecutorService scheduler Executors.newSingleThreadScheduledExecutor(); RetryExecutor execu…

前端 ---jQuery的补充

15-jQuery补充 jquery内容补充 jquery除了咱们上面讲解的常用知识点之外&#xff0c;还有jquery 插件、jqueryUI知识点 jqueryUI 官网&#xff1a; https://jqueryui.com/ jqueryUI 中文网&#xff1a; http://www.jqueryui.org.cn/ jquery插件内容包含 官网demo&#xff1a; h…

[软件工程]自我介绍----一个小菜鸡的自我介绍(C++版 手动偷笑~)

#include “iostream” using namespace std; class Lee : protected MyMother,protected MyFather { private:string ChineseName("李怡龙");string EnglishName("Lee");int age 20;string university("青海大学计算机系");string level("…

用Java编写Hadoop MapReduce任务

尽管Hadoop框架本身是使用Java创建的&#xff0c;但MapReduce作业可以用许多不同的语言编写。 在本文中&#xff0c;我将展示如何像其他Java项目一样&#xff0c;基于Maven项目在Java中创建MapReduce作业。 准备示例输入 让我们从一个虚构的商业案例开始。 在这种情况下&#…

java 大二学期总结报告_大二学生自我总结「」

大二学生导师工作总结转眼间&#xff0c;我们的大二学年就这样结束了&#xff0c;在迎接新的一学期前我们来写一份自我总结吧。下面是小编搜集整理的大二学生自我总结&#xff0c;欢迎阅读。更多资讯尽在自我总结栏目!大二学生自我总结回顾大学二年,通过良师的教导和自身的刻苦…

windows下揪出java程序占用cpu很高的线程

背景 天天搞java&#xff0c;这些监控也都知道&#xff0c;用过&#xff0c;但也没往细里追究。因为也没碰见这种问题&#xff0c;这次还是静下来走一遍流程吧。与网上基本一致&#xff0c;不过我区分了下linux和windows的不一样。我感觉基本是程序写成死循环了或者大对象分配多…