在本书的第一部分,我们的目标是对ANTLR的能力有一个整体的概述,并探索语言应用架构。一旦我们有了整体的认识,我们将在第二部分中以缓慢而系统的方式学习ANTLR,并使用大量的实际示例。要开始,让我们安装ANTLR,然后尝试在一个简单的“Hello World”语法上运行它。
安装 ANTLR
ANTLR是用Java编写的,因此在开始之前,您需要安装Java。即使您打算在其他语言(如C#或C++)中使用ANTLR生成解析器,这一点也是正确的。(我预计将来会有其他目标)ANTLR需要Java版本1.6或更高版本。
安装ANTLR本身只需下载最新的jar文件,例如antlr-4.0-complete.jar,并将其存储在适当的位置。该jar文件包含运行ANTLR工具所需的所有依赖项,以及编译和执行由ANTLR生成的识别器所需的运行时库。简而言之,ANTLR工具将语法转换为能够识别由语法描述的语言中的句子的程序。例如,给定一个用于JSON的语法,ANTLR工具会生成一个程序,该程序使用ANTLR运行时库中的一些支持类来识别JSON输入。
该jar文件还包含两个支持库:一个复杂的树布局库和StringTemplate,一个用于生成代码和其他结构化文本的模板引擎(请参见第4页的侧边栏“StringTemplate引擎”)。在4.0版本中,ANTLR仍然是用ANTLR v3编写的,因此完整的jar文件还包含先前版本的ANTLR。
StringTemplate引擎
StringTemplate是一个用于生成源代码、网页、电子邮件或任何其他格式化文本输出的Java模板引擎(也有C#、Python、Ruby和Scala的端口版本)。StringTemplate在多目标代码生成器、多个站点样式和国际化/本地化方面表现出色。它经过多年的努力在开发jGuru.com过程中逐渐发展而来。StringTemplate还生成该网站并为ANTLR v3和v4的代码生成器提供支持。有关更多信息,请参阅网站上的“About”页面。
您可以使用Web浏览器手动从ANTLR网站下载ANTLR,或者可以使用命令行工具curl来获取它。
$ cd /usr/local/lib
$ curl -O http://www.antlr.org/download/antlr-4.13.0-complete.jar
在Unix系统上,/usr/local/lib是一个存储类似ANTLR的jar文件的好目录。在Windows上,似乎没有一个标准的目录,因此您可以简单地将其存储在项目目录中。大多数开发环境要求您将jar文件放入语言应用程序项目的依赖列表中。没有配置脚本或配置文件需要更改,您只需要确保Java知道如何找到这个jar文件。
由于本书在整个过程中使用命令行,因此您需要经历设置CLASSPATH环境变量的典型繁琐过程。设置了CLASSPATH后,Java就能找到ANTLR工具和运行时库。在Unix系统上,您可以在shell中执行以下命令,或将其添加到shell的启动脚本(对于bash shell,是.bash_profile)中:
$ export CLASSPATH=".:/usr/local/lib/antlr-4.13.0-complete.jar:$CLASSPATH"
在CLASSPATH中必须包含点号(当前目录标识符),这一点非常重要。如果没有点号,Java编译器和Java虚拟机将无法看到当前目录中的类。在本书中,您将一直从当前目录编译和测试代码。
您现在可以通过在没有参数的情况下运行ANTLR工具来检查ANTLR是否已正确安装。您可以使用java -jar选项直接引用jar文件,或直接调用org.antlr.v4.Tool类。
$ java -jar /usr/local/lib/antlr-4.13.0-complete.jar # launch org.antlr.v4.Tool
ANTLR Parser Generator Version 4.13.0
-o ___ specify output directory where all output is generated
-lib ___ specify location of grammars, tokens files
-atn generate rule augmented transition network diagrams
-encoding ___ specify grammar file encoding; e.g., euc-jp
-message-format ___ specify output style for messages in antlr, gnu, vs2005
-long-messages show exception details when available for errors and warnings
-listener generate parse tree listener (default)
-no-listener don't generate parse tree listener
-visitor generate parse tree visitor
-no-visitor don't generate parse tree visitor (default)
-package ___ specify a package/namespace for the generated code
-depend generate file dependencies
-D<option>=value set/override a grammar-level option
-Werror treat warnings as errors
-XdbgST launch StringTemplate visualizer on generated code
-XdbgSTWait wait for STViz to close before continuing
-Xforce-atn use the ATN simulator for all predictions
-Xlog dump lots of logging info to antlr-timestamp.log
-Xexact-output-dir all output goes into -o dir regardless of paths/package
$ java org.antlr.v4.Tool # launch org.antlr.v4.Tool
ANTLR Parser Generator Version 4.13.0
-o ___ specify output directory where all output is generated
-lib ___ specify location of grammars, tokens files
-atn generate rule augmented transition network diagrams
-encoding ___ specify grammar file encoding; e.g., euc-jp
-message-format ___ specify output style for messages in antlr, gnu, vs2005
-long-messages show exception details when available for errors and warnings
-listener generate parse tree listener (default)
-no-listener don't generate parse tree listener
-visitor generate parse tree visitor
-no-visitor don't generate parse tree visitor (default)
-package ___ specify a package/namespace for the generated code
-depend generate file dependencies
-D<option>=value set/override a grammar-level option
-Werror treat warnings as errors
-XdbgST launch StringTemplate visualizer on generated code
-XdbgSTWait wait for STViz to close before continuing
-Xforce-atn use the ATN simulator for all predictions
-Xlog dump lots of logging info to antlr-timestamp.log
-Xexact-output-dir all output goes into -o dir regardless of paths/package
每次键入这些java命令来运行ANTLR都会很麻烦,所以最好创建一个别名或shell脚本。在整本书中,我将使用alias antlr4,您可以在Unix系统上按如下定义:
$ alias antlr4='java -jar /usr/local/lib/antlr-4.13.0-complete.jar'
或者,您可以将以下脚本放入/usr/local/bin目录中
$ sudo vim /usr/local/bin/antlr4.sh
键入下面内容:
#!/bin/sh
java -cp "/usr/local/lib/antlr4-complete.jar:$CLASSPATH" org.antlr.v4.Tool $*
无论哪种方式,您只需输入antlr4即可。
$ antlr4
ANTLR Parser Generator Version 4.13.0
-o ___ specify output directory where all output is generated
-lib ___ specify location of grammars, tokens files
-atn generate rule augmented transition network diagrams
-encoding ___ specify grammar file encoding; e.g., euc-jp
-message-format ___ specify output style for messages in antlr, gnu, vs2005
-long-messages show exception details when available for errors and warnings
-listener generate parse tree listener (default)
-no-listener don't generate parse tree listener
-visitor generate parse tree visitor
-no-visitor don't generate parse tree visitor (default)
-package ___ specify a package/namespace for the generated code
-depend generate file dependencies
-D<option>=value set/override a grammar-level option
-Werror treat warnings as errors
-XdbgST launch StringTemplate visualizer on generated code
-XdbgSTWait wait for STViz to close before continuing
-Xforce-atn use the ATN simulator for all predictions
-Xlog dump lots of logging info to antlr-timestamp.log
-Xexact-output-dir all output goes into -o dir regardless of paths/package
如果您看到帮助信息,那么您已经准备好快速测试ANTLR了!
执行ANTLR和测试识别器
这是一个简单的语法,用于识别像"hello parrt"和"hello world"这样的短语:
$ mkdir /tmp/test
$ cd /tmp/test
$ vim Hello.g4
键入下面内容:
grammar Hello;
r : 'hello' ID ;
ID : [a-z]+ ;
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines, \r (Windows)
测试跑一把看看:
$ antlr4 Hello.g4
$ ls
Hello.g4 HelloBaseListener.java HelloLexer.tokens
Hello.interp HelloLexer.interp HelloListener.java
Hello.tokens HelloLexer.java HelloParser.java
编译一下:
$ javac *.java
$ ls
Hello.g4 HelloLexer.class HelloListener.java
Hello.interp HelloLexer.interp HelloParser$RContext.class
Hello.tokens HelloLexer.java HelloParser.class
HelloBaseListener.class HelloLexer.tokens HelloParser.java
HelloBaseListener.java HelloListener.class
在Hello.g4上运行ANTLR工具将生成一个由HelloParser.java和HelloLexer.java实现的可执行的识别器,但我们没有一个主程序来触发语言识别(我们将在下一章中学习解析器和词法分析器的概念)。这在项目开始阶段是典型情况。在构建实际应用程序之前,您可以尝试几种不同的语法。
为了避免在测试每个新的语法时都需要创建一个主程序,ANTLR提供了一个名为TestRig的运行时库中的灵活测试工具。它可以显示关于识别器如何匹配来自文件或标准输入的输入的大量信息。TestRig使用Java反射来调用编译的识别器。与之前一样,创建一个方便的别名或批处理文件是个好主意。在整本书中,我将称其为grun(但您可以根据需要自己命名)。
$ alias grun='java org.antlr.v4.runtime.misc.TestRig'
测试工具(TestRig)接受一个语法名称、一个类似于main()方法的起始规则名称,以及一些选项,用于指定我们想要的输出。假设我们想要打印在识别过程中创建的标记(tokens)。标记是词汇表中的符号,例如关键字"hello"和标识符"parrt"。为了测试语法,请按以下方式启动grun:
$ grun Hello r -tokens hello parrt
[@0,0:4='hello',<1>,1:0] # these three lines are output from grun [@1,6:10='parrt',<2>,1:6]
[@2,12:11='<EOF>',<-1>,2:0]
在输入grun命令后,计算机会耐心地等待您输入"hello parrt",然后按回车。在那时,您必须输入文件结束符(end-of-file character)以终止从标准输入读取;否则,程序将永远等待您。一旦识别器读取了所有的输入,TestRig根据grun命令中使用的-tokens选项打印出标记(tokens)列表。
输出的每一行表示一个单独的标记,并显示我们对该标记的所有了解。例如,[@1,6:10='parrt',<2>,1:6]表示该标记是第二个标记(从0开始计数),从字符位置6到10(从0开始计数,包括6和10),文本为parrt,标记类型为2(ID),在第1行(从1开始计数),在字符位置6(从0开始计数,将制表符视为单个字符)。