antlr java

在我以前的文章中,有一段时间我写了关于使用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

antlr java

antlr java_使用ANTLR和Java创建外部DSL相关推荐

  1. 使用ANTLR和Java创建外部DSL

    在以前的一段时间里,我曾写过有关使用Java的内部DSL的文章. 在Martin Fowler撰写的< 领域特定语言 >一书中,他讨论了另一种称为外部DSL的DSL,其中DSL是用另一种语 ...

  2. hive 创建外部表产生java_(一)Hive表(管理表、外部表)的创建规则

    建表语句: CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name [(col_name data_type [COMMENT col_comment], ...

  3. java 执行外部命令 苹果_Java中执行外部命令

    在项目中执行一个linux的shell脚本,于是需要在java环境下执行外部命令如系统命令.linux命令的需求,本人小小研究了一下,又上网查了一些资料先整理如下. java执行外部命令主要依赖两个类 ...

  4. java中属性外部化_用Java可外部化

    java中属性外部化 在理解Externalizable接口之前,您需要了解序列化.您可以在java中的序列化上阅读有关序列化的更多信息. Java提供了一种称为序列化的机制,以按字节的有序或字节序列 ...

  5. martin fowler_用Java和Java 8创建内部DSL,采用Martin Fowler的方法

    martin fowler 目前,我正在阅读Martin Fowler撰写的有关DSL- 特定领域语言的精彩书籍. 围绕DSL的嗡嗡声,围绕轻松支持DSL创建的语言,以及DSL的使用,使我好奇地了解和 ...

  6. 用Java和Java 8创建内部DSL,采用Martin Fowler的方法

    目前,我正在阅读Martin Fowler撰写的有关DSL- 特定于域的语言的精彩书籍. 围绕DSL的嗡嗡声,围绕轻松支持DSL创建的语言,以及DSL的使用使我好奇地了解和了解DSL的这一概念. 到目 ...

  7. java什么时候创建进程,Java创建进程

    Java创建进程 1 进程的概念 1 1.1 进程的概念 1 1.2 进程的特征 1 1.3 进程与线程区别 1 2 进程的创建 1 2.1 JAVA进程的创建 1 2.1.1 ProcessBuil ...

  8. java创建型_Java创建型模式

    Java创建型模式 在软件工程中,创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象.基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度.创建型模式通过以某种方式控制 ...

  9. java使用外部库_在Java中使用外部库

    java使用外部库 Java附带了一组核心库,其中包括定义常用数据类型和相关行为的库,例如String或Date : 与主机操作系统进行交互的实用程序,例如System或File : 有用的子系统来管 ...

最新文章

  1. 关于StartCoroutine的简单线程使用
  2. 目前有量子计算机,中国“祖冲之”刚刚成为当前最强大的量子计算机
  3. SpringBoot中整合Mail实现发送模板邮件
  4. USACO1.5 Number Triangles(numtri)
  5. Web实时通信,SignalR真香,不用愁了
  6. winform Outlookbar
  7. 请查收~微信春节聊天彩蛋 微信群的卖萌小神器
  8. [PHP] - visitFile()遍历指定文件夹
  9. 三种常用的MySQL建表语句
  10. 2015最新Linkedin人才趋势报告
  11. 九九乘法表——C语言
  12. 使用微PE制作启动U盘重装系统教程
  13. php web应用 开发工具,phpEclipse开发工具(二 web应用)
  14. 2020身高体重标准表儿童_2020儿童身高标准表出炉,10岁长到1米4才合格,你家娃达标了吗...
  15. CAXA图文档2007服务器端,CAXA图文档客户端系统管理员操作.doc
  16. 头条小程序Component构造器
  17. ffmpeg-linux录音录像
  18. Android网络通讯之Retrofit
  19. “一键GHOST”傻瓜式系统备份与恢复
  20. EditPlus字体放大方法

热门文章

  1. Juice Extractor dp
  2. 33、JAVA_WEB开发基础之会话机制
  3. 面试官问:为什么 Java 线程没有 Running 状态?我懵了
  4. String 使用不当可能导致内存泄露
  5. 关于Icon,Image,ImageIcon的简单的对比参考 上篇
  6. Oracle入门(十三)之SQL的DML
  7. 简单介绍Java中Comparable和Comparator
  8. 3分钟了解“关联规则”推荐
  9. “老师,请您多关注一下我吧!!!”
  10. Photoshop基本操作