资源描述
Antlr简介
06通讯软件 06382027 郑毅
本文重要简介了什么是ANTLR,以及ANTLR使用,其中ANTLR使用涉及了ANTLR安装及使用,ANTLR语法文献解析,ANTLR规则(RULE)解析,ANTLR语法实例—SensorSQL,ANTLR Studio及其功能简介等。
Antlr(ANother Tool for Language Recognition)是一种工具,前身是 PCCTS,它为咱们构造自己辨认器(recognizers)、编译器(compiler)和转换器(translators)提供了一种基本。通过定义自己语言规则,Antlr可觉得咱们生成相应语言解析器,这样便可以省却了自己全手工打造劳苦。
它是这样一种工具,它可以接受文法语言描述,并能产生辨认这些语言语句程序。作为翻译程序一某些,你可以使用简朴操作符和动作来参数化你文法,使之告诉ANTLR如何去创立抽象语法树(AST)和如何产生输出。ANTLR懂得如何去生成辨认程序,语言涉及Java,C++,C#和不久Python。
ANTLR懂得如何构建辨认程序,这些程序可以对如下三种不同输入应用文法构造:(i) 字符流,(ii) 记号流,和(iii)两维树构造。很自然它们分别与词法分析程序(lexers,如下简称lexer),语言解析程序和树遍历程序向匹配。这个用于定义这些语法元语言,在所有状况下几乎同样。
一旦你对ANTLR和类似工具比较顺手,你会开始以一种新目光来看编程。许多任务强烈需要语言解决方案,而不是采用老式编程语言做法。例如,这些过程注解都是用特伦斯标记语言写。而ANTLR则能来将文本(内含某些额外东西和转换)转化为HTML,PDF或者其她那些生成程序文献格式。
最后,ANTLR只是一件工具,仅仅这些。 虽然它能通过将容易理解乏味某些自动化来协助你创立软件,但却不能企图让你指定整个编译器。例如,在单个描述里就不行。那些宣称此类事情非常伟大,可觉得发布刊物文章编写惊人“一揽子解决方案”,却会悲惨失败在实际项目中。
词法分析器(Lexer)
词法分析器又称为Scanner,Lexical analyser和Tokenizer。程序设计语言普通由核心字和严格定义语法构造构成。编译最后目是将程序设计语言高层指令翻译成物力机器或虚拟机可以执行指令。此法分析器工作是分析量化那些本来毫无意义字符流,将她们翻译成离散字符组(也就是一种一种Token)括核心字,标记符,符号(symbols)和操作符供语法分析器使用。
语法分析器(Parser)
编译器又称为Syntactical analyser。在分析字符流时候,Lexer不关怀所生成单个Token语法意义及其与上下文之间关系,而这就是Parser工作。语法分析器将收到Tokens组织起来,并转换成为目的语言语法定义所容许序列。
无论是Lexer还是Parser都是一种辨认器,Lexer是字符序列辨认器而Parser是Token序列辨认器。她们在本质上是类似东西,而只是在分工上有所不同而已。
ANTLR
ANTLR将上述两者结合起来,它容许咱们定义辨认字符流词法规则和用于解释Token流词法分析规则。然后,ANTLR将依照顾客提供语法文献自动生成相应词法/语法分析器。顾客可以运用她们将输入文本进行编译,并转换成其她形式(如AST—Abstract Syntax Tree,抽象语法树)。
Antlr使用
安装及使用
到下载最新版本ANTLR开发包和源码(例如版本3.01)。将antlr-3.0.1.jar所在目录配备到你环境变量中,写好语法文献(例如SensorSQL.g),运营命令“java antlr.Tool SensorSQL.g”就可以获得自动生成语法/词法分析器。
ANTLR语法文献解析
下面咱们对图中所描述ANTLR语法文献做某些详细分析。为了更好使用ANTLR,你还可如下载ANTLREclipse插件来协助你完毕工作。
1. header域:所有出当前这里某些,都会出当前由ANTLR编译之后生成Java文献最顶部。在本例中你可以将包名和其她信息放到这一区域中,生成成果如由面相应代码某些所示。
2. 你在这一某些所提供内容对于文献中每个语法都是唯一。这一区域内容将出当前实际类定义之前。也就是说,两个import仅属于类CalcParser,而不属于在同一种文献中定义其她类(如CalcLexer)
3. 这里是语法定义某些,你同样可以将它当作是类定义。
4. 在Option域中,你可觉得你语法提供可选项。例如与否建立缺省抽象语法树,指定LL(K)中参数k值(缺省为1)等等,更详细参数请参阅ANTLR自带手册。
5. Token某些用来声明那些在词法分析器中没有被声明“想象”token。这些信息通惯用在TreeParser中指定“想象”节点。
6. 这是另一种Action区,ANTLR将会忠实地将这一区域内信息放置到类定义当中,相称于类成员办法,重要为顾客提供一种在Parser种定制可扩展办法途径。
ANTLR规则(RULE)解析
在ANTLR语法文献中,一种规则定义是与一种由ANTLR生成Java源文献相相应。
1,2,3,4:正如你所看到那样,咱们可以在一种规则定义中作与一种函数等价所有事情。咱们可觉得规则指定参数(像上面int a),制定返回值(int c),甚至抛出一种异常。从右半面咱们可以清晰地看到,所有在规则中定义内容都被忠实而精确翻译到Java源文献相应位置。
5:这一可选某些为咱们提供了指定某些可选参数能力。例如图中所示代表告诉ANTLR在生成代码时候不要生成缺省错误解决某些,这某些将由顾客自己负责。
7:在异常解决某些,咱们可以指定自定义异常解决办法。像这里就仅仅是打印错误栈信息。
ANTLR语法实例—SensorSQL
SensorSQL是一种自定义简化版SQL语言,它所支持语法定义这里就不详细列出了,我只是给出查询示例:
普通,编译一种查询目是要把它转化成某种被查询设备可以理解形式。普通做法有两种,一种是像在上一节中提到那样,写好详细语法规则,在ANTLR生成相应Java文献之后,就可以直接使用其运营成果。这样例子有诸多,其中最典型就是对于算数表达式解析了。对于形如1+2-3*4/5^6这样表达式,只要写好语法规则,就可以在解析过程中直接得到运算成果:一方面ANTLR将其编译成逆波兰构造-- ( - ( + 1 2 ) ( / ( * 3 4 ) ( ^ 5 6 ) ) ) ;在生成语法树过程中,同步计算表达式值,即类似于2.3节中看到表达式计算。成果如下:
但是这样作有一种缺陷,就是在诸多状况下,你也许并不懂得要用什么样办法来解决。因此当真正要开始写解决代码时候,就要受限于已有Parser/Lexer中代码。一旦要有所修改,就要重新编译语法文献,生成新Java代码,不胜繁琐。并且,一旦解决过程有误,就要重复调试修改Antlr生成代。自动生成代码嘛,构造着实也不怎么样,调试时候也麻烦。因此如果效率容许话,就没有必要让Antlr作额外工作,干脆就专心于做她语法分析也就是了,其她工作等到生成语法树之后再怎么遍历或者折腾都可以嘛J。
上图就是刚才演示SensorSQL语法分析之后产生成果。在产生这个成果之后,我需要将每一种语法元素翻译成字节序列打包发送给传感器网络。这时候,为了保证Where语句中优先级,你就可以按照ANTLR文档中关于生成语法树一章,生成类似于这样构造,然后只需前序遍历这颗语法树Where某些就可以达到目,至于其她某些,顺序遍历一遍就好了。
ANTLR Studio
有了前面基本之后,咱们就可以开始真正工作了。但是用“记事本或Editplus+命令行”或者干脆写个ANT脚本也不是不可以,但是总觉得在集成化IDE满天飞时代用这个方式有点过于原始,幸好Placid System 为咱们提供了一种Eclipse插件来使咱们有机会直接走出原始社会。下载地址为:,当前最新版本是1.1.0。唯一令人遗憾是这个插件虽然功能很完善,却是要收费,否则只有11天试用期。
ANTLR Studio插件安装
Eclipse下插件安装自不必多说,要注意是从Placid System网站上提供license文献,下载之后它名字为license.lic.txt,要把它后缀名.txt去掉,然后放到ECLIPSE_DIR\plugins\AntlrStudio_x.x.x 目录(这里 x.x.x 是版本号,例如 - 1.1.0)。安装成功之后在Eclipse工具栏上会浮现一种词法分析器导航按钮:
当右键单点击你工程时,你会发现控制与否使用ANTLR Studio开关:
当打开一种文法文献之后,可以看到如下界面:
在右面大纲窗口,列有所有Parser和Lexer元素,可以看到Protected Token(例如Number)和其她普通Token是不同样;在左面,不同区域是用不同颜色块加亮来区别。
功能简介
ANTLR Studio在Eclipse Help提供了比较详尽文档描述,因此这里我只简介某些1.1.0版本新功能。
l 完全支持ANTLR3.0.1,并支持将之前工程自动升级到1.1.0版本。
l Syntax Diagram View,可以以便查看所输入语法构造。
l 改进了Debug功能,可以调试比较大文法文献。而在这之前,如果一种文法文献很大话,ANTLR Studio就会抛出异常。
l 支持自动代码补全功能,提供一种ANTLR文档比较全面提示信息(如下所示)。
语法图表视图(Syntax Diagram View)
在Window->Show View->Other中选取显示这个视图之后,你就可以使用这个很酷功能了
运用这个视图,你可以很容易看到你定义语法语法构造,例如,我SELECT语句定义如下
你只需要将光标标放到selectStatement规则任意位置,就可以在Syntax Diagram View中看到:
于是完整语法构造清晰显示在了咱们面前。这时你只需要将光标放到脱字符号(^)上面(注:脱字符号用于指明在生成语法树时候,脱字符号所在SubRule要作为树或子树根节点):就会看到:
相应SubRule被加亮成粉红色,而如果你光标放到位置是一种Token话就会变成淡蓝色,简直太酷了。
增强Debug功能
想要启动或关闭ANTLR StudioDebug功能,需要完毕如下环节:
l 在工程中启用/取消ANTLR Studio
l 右键单击工程,打开“属性”中ANTLR Studio选项卡。
l 选取/取消'Enable debugging in grammar files'
做完这些后,咱们就可以痛快使用其Debug功能了。与调试其她Java文献同样,咱们可以在语法文献任意位置插入断点:
当程序运营至断点之后,咱们同样可以像调试普通应用程序同样使用诸如“跳过”,“继续”等Java应用程序Debug方式来进行,十分以便和顺手。
注:以上资料均来于网络,鄙人收集整顿。
ANTLR中文手册
06通讯软件 06382027 郑毅
本文重要概括了某些惯用ANTLR用法,其中有Antlr重要类,Antlr文法文献形式,生成Java类,如何生成Java类,如何执行以及元语言词汇表。
一、Antlr重要类:
Antlr中有重要类有两种(其实尚有一种TreeLexer)
Lexer:文法分析器类。重要用于把读入字节流依照规则分段。既把长面条依照你要尺寸切成一段一段:)并不对其作任何修改。
Parser:解析器类。重要用于解决通过Lexer解决后各段。某些详细操作都在这里。
二、Antlr文法文献形式:
Antlr文献是*.g形式,即以g为后缀名。
例如:t.g
class P extends Parser;
start Rule
:n:NAME
{System.out.println("Hithere," n.getText());}
;
class L extends Lexer;
//one-or-morelettersfollowedbyanewline
NAME:(’a’……’z’|’A’……’Z’) NEWLINE
;
NEWLINE
:’\r’’\n’//DOS
|’\n’//UNIX
;
详细成分分析:
1、总体构造
ClassPextendsParser
ClassLextendsLexer
两行同JAVA继承同样,P继承Parser类;L继承Lexer类。每个.g文献只能各有一种。
2、Lexer类分析
普通按照类型名:(匹配详细规则)形式构成。是分隔字节流根据。同步可以看到里面可以互相引用。如本例中类型名NEWLINE出当前NEW匹配规则中。
3、Parser类分析
普通按照
起始规则名:
规则实例名:类型名或规则名
{Java语句……;}
;
起始规则名:任意。
规则实例名:就象Java中“Strings;”s同样。规则实例名用于在之后JAVA语句中调用。
类型名或规则名:可以是在Lexer中定义类型名,也可以是Parser中定义规则名。感觉就像是int与Integer区别。
Java语句:指当满足当前规则时所执行语句。Antlr会自动嵌入生成java类中。
三、生成Java类
1、从.org上下载antlr-x.x.x.jar
2、配备环境变量:classpath=.;x:\jdk\lib\tools.jar;x:\antlr-x.x.x.jar
3、在t.g所在目录下执行:
javaantlr.Toolt.g
会在当前目录下生成如下文献:
L.java:Lexer文法分析器java类。
P.java:Parser解析器java类。
PTokenTypes.java:Lexer中定义类型详细化,供Parser解析器调用。
PTokenTypes.txt:当外部(如t2.g)要调用当前类型或规则时要用到本文献。
四、执行
1、编写Main类
importjava.io.*;
classMain{
publicstaticvoidmain(String[]args){
try{
Llexer=newL(newDataInputStream(System.in));
Pparser=newP(lexer);
parser.startRule();
}catch(Exceptione){
System.err.println("exception:" e);
}
2、执行
c:\>javac*.java
c:\>javaMain
Terence
^Z
Hithere,Terence
c:\>
元语言词汇表
空格定义 空格,tab符号和换行符号在ANTLR分隔诸如标记符这样词汇符号时作为分隔符。在这之外,它们是被忽视。例如,“FirstName LastName”对ANTLR来说两个标记符而不是一种标记符,空格,然后再接着一种标记符。
注释
ANTLR 接受C语言风格块注释和C++风格行注释。在语法类和规则中,Java风格文档注释也是可以接受,在需要时候,这些注释可以被传递给生成输出文献。例如
/**This grammar recognizes simple expressions
* @author Terence Parr
*/
class ExprParser;
/**Match a factor */
factor :... ;
字符集
字符常数像Java中那样被拟定。它们包括八进制转义字符集(e.g.,'\377'),Unicode字符集(e.g.,'\uFF00'),和能被Java辨认惯用字符转义('\b','\r','\t','\n','\f','\'','\\')。在词法分析器规则中,单引号代表一种可以在输入字符流中能得到匹配字符。在语法分析器中是不被支持单引号字符。
文献结束标志 EOF 标记用语法分析器规则中自动生成:
rule :(statement)+ EOF;
你可以在词法分析器规则动作中检测EOF_CHAR符号:
// make sure nothing but newline or
// EOF is past the #endif
ENDIF
{
boolean eol=false;
}
: "#endif"
( ('\n' | '\r') {eol=true;} )?
{
if (!eol) {
if (LA(1)==EOF_CHAR) {error("EOF");}
else {error("Invalid chars");}
}
}
;
当你将文献结束当一种字符来检测时,它事实上并不是一种字符,而是一种条件。
你可以在你词法分析器语法中覆盖 CharScanner.uponEOF()函数:
/** This method is called by YourLexer.nextToken()
* when the lexer has
* hit EOF condition. EOF is NOT a character.
* This method is not called if EOF is reached
* during syntactic predicate evaluation or during
* evaluation of normal lexical rules,which
* presumably would be an IOException. This
* traps the "normal" EOF * condition.
*
* uponEOF() is called after the complete evaluation
* of the previous token and only if your parser asks
* for another token beyond that last non-EOF token.
*
* You might want to throw token or char stream
* exceptions like:"Heh,premature eof" or a retry
* stream exception ("I found the end of this file,
* go back to referencing file").
*/
public void uponEOF()
throws TokenStreamException,CharStreamException
{
}
文献结束条件是一种位比特。由于Terence 将-1当作一种字符而不是一种整型数。(-1 是 '\uFFFF').
字符串
字符串常数一种由双引号括起来一系列字符。在字符串中字符可以是作为字符也同样合法转义字符(八进制,Unicode等)。当前,ANTLR事实上不容许Unicode出当前字符串常量中(你不得不用转义符)。这是由于在anglr.g文献中设定charVocabulary选项为ascii.
在词法分析器规则中,字符串被解释成可以在输入流中匹配一系列字符 (例如.,"for" 等于 'f' 'o' 'r').
在语法分析器规则中,字符串代表一种个标记(tokens),并且每个独立字符串被分派一种标记类型。然而,ANTLR不会创立一种词法分析器规则来匹配这些字符串。相反,ANTLR将这些字符串输入到一种于词法分析器关联字符常量表中。ANTLR将针对字符常量表来产生代码检测每个标记中文本,在手动关掉语法分析器对该标记解决之前获得一种匹配时,会变化标记类型。你也可以执行手动检测――自动代码生成可以通过词法分析器选项控制。
你也许想在你动作中使用这些字符串常量标记类型值,例如在错误解决器同步某些。对于只由字母字符构成字符串常量来说,这个字符串常量值将是一种形如LITERAL_xxx常量值,这里xxx是这个标记名字。例如,文字“return”将有一种LITERAL_return值与之关联。你也可以用标记节(tokens section)分派一种特定标号给这个文字。
标记引用
以大写字符开头标记符称为标记引用。接下来字符可以是任何字符,数字或下划线。在语法分析器规则中一种标记引用将导致匹配特定标记。在词法分析器中标记引用将导致调用一种词法规则来匹配该标记字符。换句话说,在词法分析器中标记引用将对当作一种规则引用。
标记定义
在词法分析器中标记定义由和语法规则中相似定义。但是当做标记而不是语法规则。例如,
class MyParser extends Parser;
idList :( ID )+; // parser rule definition
class MyLexer extends Lexer;
ID :( 'a'..'z' )+ ; // token definition
规则引用
以小写字母开头标记符是为ANTLR语法规则。接下来字符可以是任意字母,数字或下划线。词法规则不能引用语法规则。
动作.
在尖括号中字符序列是语义动作(也许是嵌套)。在字符串和字符中尖括号不是动作分隔符。
动作参数
在方括号中字符序列是动作参数(也许是嵌套)。在字符串和字符中方括号不是动作分隔符。在[]中参数是用被生成语言语法定义,并且用逗号分开。
codeBlock
[int scope,String name] // input arguments
returns [int x] // return values
:... ;
// pass 2 args,get return
testcblock
{int y;}
: y=cblock[1,"John"]
;
许多人喜欢咱们用普通括号来括住参数,但是括号在EBNF中已经被较好用来定义语法组符号(grammatical grouping symbols)。
符号 下面表记录了在ANTLR中使用标点符号和核心字。
符号
描述
(...)
子规则
(...)*
闭包子规则(零和各种)
(...)+
正闭包子规则(一种和各种)
(...)?
可选(零个和一种)
{...}
语义动作
[...]
规则参数
{...}?
语义谓词
(...)=>
语法谓词
|
可选符
..
范畴符
~
非
.
通配符
=
赋值
:
标号符,规则开始
;
规则结束
<...>
元素选项
class
语法类
extends
指定语法基类
returns
指定返回类型
options
options 节
tokens
tokens 节
header
header 节
tokens
token 定义节
Header 节
一种header节包括了某些将直接被替代到输出语法分析器中源码,这些源码将在所有ANTLR生成代码之前。这个重要用在C++输出中,由于C++需要某些元素在引用之前必要被声明。在Java中,这可以用来为最后语法分析器指定某些包文献。一种header节看起来像下面这样:
header {
source code in the language generated by ANTLR;
}
header 节是语法文献第一种节。依照选取目的语言不同,不同类型header节都是也许浮现。看各自附录。
语法分析器类定义
所有语法规则必要和一种语法分析器关联。一种语法文献(.g)只包括一种语法分析器类定义(和词法分析器和树遍历器一起)一种语法分析器定义在它选项(options)和规则定义之前。一种语法文献中语法分析器定义普通是这个样子:
{ optional class code preamble }
class YourParserClass extends Parser;
options
tokens
{ optional action for instance vars/methods }
parser rules...
当在面向对象语言中生成代码时,语法分析器类将导致在输出中是一种类,规则都会变成这个类成员函数。在C中,类将导致生成一种构造,某些名字混淆(name-mangling)算法将用在上面使最后规则函数是全局唯一。
可选类预定义可以是包括在{}中任意文本。这个预定义,如果它存在话,将被直接输出到生成类文献中,并且在类定义之前。
封闭尖括号不能用来分隔类,由于一种左尖括号在文献顶就很难跟踪与之匹配右括号在文献时。相反,一种语法分析器类假定是持续,懂得遇到下一种类语句。
你可以指定语法分析器超类,它将作为被生成语法分析器超类。这个超类必要是完整定义,在双引号中。它自己必要是antlr.LlkParser子类。例如
class TinyCParser extends Parser("antlr.debug.ParseTreeDebugParser");
词法分析器类定义
一种语法分析器类将导致一种懂得如何依照输入流标记来应用语法构造语法分析器对象。为了执行词法分析,你需要指定一种词法分析器类,它描述了如何将输入流分离成标记流。它语法类似于语法分析器类:
{ optional class code preamble }
class YourLexerClass extends Lexer;
options
tokens
{ optional action for instance vars/methods }
lexer rules...
包括在词法分析器中词法规则在产生类中变成成员办法。每个语法文献(.g)只包括一种词法分析器。语法分析器和词法分析器可以以任何顺序浮现。
可选类开头是括在{}中任意文本。这个开头某些,如果它存在,将输出到被生成类文献中,在类定义之前。
你可以定义一种词法分析器超类,它可以被用来作为产生词法分析器超类。这个超类将是完整定义(fully-qualified),在双引号中,它自身是antlr.CharScanner子类。
树分析器定义
一种树分析器像语法分析器,不同是它解决二维由节点构成抽象语法树,而不是解决由标记构成流。树分析器必要唯一指定给语法分析器,除非规则定义中包括特殊形式致使它递归下降到树中。同样,一种特定语法文献(.g)中仅仅包括一种树分析器。
{ optional class code preamble }
class YourTreeParserClass extends TreeParser;
options
tokens
{ optional action for instance vars/methods }
tree parser rules...
你可以定义一种树分析器超类,它可以被用来作为产生树解析器超类。这个超类将是完整定义(fully-qualified),在双引号中,它自身是antlr.TreeParser子类
注:以上资料均来于网络,鄙人收集整顿。
计算器及文法文献
06通讯软件 06382027 郑毅
本文重要简介了运用ANTLR达到计算器需求以及运用ANTLR生成C++描述分析程序。
一.运用ANTLR达到计算器需求
1.运算符:+,-,×,÷,(,)
2.支持整型和浮点型
测试样例
1.1+ 2 * 3 + 5 - 4/2
2.(1 + 2) * 3 + 5 - 4 / (2+2)
3.(1.2*2.5)+8/(4-3)*2.7
下面就让咱们动手完毕一种计算器,:)
先搭个框架。文献名是calc.g
options {
language = “Cpp”;
}
class CalcParser extends Parser;
class CalcLexer extends Lexer;
这些就是基本框架了。
options里设立language为”Cpp”,表达要生成c++代码。
CalcParser是咱们计算器语法解析类,继承ANTLR里Parser类。
同理,CalcLexer是词法分析类,继承ANTLR里Lexer类。
接着定义计算器词法规则。
一方面是运算符。
PLUS :‘+’;
SUB :‘-’ ;
MUL :‘*’ ;
DIV :‘/’ ;
LPAREN :‘(’ ;
RPAREN :‘)’ ;
接着是操作数。
NUM :(’0′..’9′)+ { $setType(INT);} (’.’ (’0′..’9′)+ { $setType(REAL);} )?;
(注:$setType是ANTLR内置函数,用来设立token类型。就是当匹配到(’0′..’9′)*后,设立token类型为INT,当发现背面跟着小数点和数字后,重新设立token类型为REAL。)
WS :(’ ‘
| ‘\t’
| ‘\n’
| ‘\r’)+
{
$setType(ANTLR_USE_NAMESPACE(antlr)Token::SKIP);
}
;
statement :mexpr ( (PLUS | SUB) mexpr )*
;
mexpr :expr ( (MUL | DIV) expr )*
;
expr :INT
| REAL
| LPAREN statement RPAREN
;
一种计算器语法程序就写好了。让咱们来生成c++代码,实际测试一下.
$java -cp /usr/share/java/antlr.jar antlr.Tool calc.g
ANTLR Parser Generator Version 2.7.6 (0528) 1989-
$ls
calc.g CalcParser.cpp CalcParserTokenTypes.txt
CalcLexer.cpp CalcParser.hpp
CalcLexer.hpp CalcParserTokenTypes.hpp
$
咱们可以看到,生成了6个文献。
CalcTest.cpp:
#include “CalcLexer.hpp”
#include “CalcParser.hpp”
#include
using namespace std;
using namespace antlr;
int main()
{
try {
CalcLexer lexer(cin);
CalcParser parser(lexer);
parser.statement();
} catch (exception &e) {
cout << e.what() << endl;
}
}
编译之:
$g++ -o Calc CalcTest.cpp CalcParser.cpp CalcLexer.cpp -lantlr
测试用例:
$ ./Calc
1 + 2 * 3 + 5 - 4/2
$ ./Calc
(1 + 2) * 3 + 5 - 4 / (2+2)
$ ./Calc
(1.2*2.5)+8/(4-3)*2.7
$
二.运用ANTLR生成C++描述分析程序
文法文献
文法就是语言辨认规则。它是ANTLR生成程序根据。文法文献是ANTLR核心,是程序员和ANTLR进行交流接口。
文法文献编写基本是面向被解决问题。程序员只需要集中精力思考解决问题逻辑,而不是羁绊于某种程序设计语言实现细节,因而减少了浮现错误也许性。
文法文献语法简介(转自ANTLR中文手册)
本文只是简朴地简介一种文法文献语法,详细内容可以参阅ANTLR有关文档。
文法文献普通涉及header块、options块、文法分析器类(parser)及规则定义、词法分扫描器类(lexer)及token定义。其中最为重要是规则和token定义。
规则定义形式和编译理论中扩展巴科斯范式(EBNF)极为相似,涉及规则名、规则体、一种用作结束标志分号和异常解决某些(可省略)。例如如下规则就描述了C语言中赋值语句语法:
assignment_stat:
id '=' expr ';'
;
其意义是:一条赋值语句是由一种id、一种等号、一种表达式和一种分号顺序构成。
Token定义办法与规则类似。例如如下token定义就表达一种十进制整数:
NUM:
('1'..'9')('0'..'9')*
;
其意义是:数字(NUM)第一字符是‘1’到‘9’中一种字符,背面是0个或各种‘0’到‘9’之间字符。
需要注意一点是:规则名字必要是小写字母开始,而token名字则必要是大写字母开始。
设定ANTLR生成语言(转自ANTLR中文手册)
ANTLR有诸多选项,可以通过在文法文献中options块中进行设立,其中涉及ANTLR最后身成语言。如果要生成C++描述分析器程序,就要如下设定:
options
{
language="Cpp";
// Other options
}
language选项默认值是“Java”。如果您但愿生成程序是C#,将language设为“Csharp”就可以了。
C++程序例子
下面就给出一种ANTLR生成C++描述分析器实例。该分析器功能是分析顾客输入一种算术表达式,给出该表达式最后成果。在该表达式中容许浮现运算符除了加减乘除之外,还涉及求幂运算符“^”,以及sin、cos和tan三个三角函数。
编写文法文献
header{
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
}
options
{
language="Cpp";
}
class ExprParser extends Parser;
{
}
// rules
expr returns [double value=0
展开阅读全文