版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
-.z.Antlr简介06通讯软件06382027毅本文主要介绍了什么是ANTLR,以及ANTLR的使用,其中ANTLR的使用包括了ANTLR的安装及使用,ANTLR语法文件解析,ANTLR规则(RULE)解析,ANTLR语法实例—SensorSQL,ANTLRStudio及其功能介绍等。Antlr(ANotherToolforLanguageRecognition)是一个工具,前身是PCCTS,它为我们构造自己的识别器(recognizers)、编译器(piler)和转换器(translators)提供了一个基础。通过定义自己的语言规则,Antlr可以为我们生成相应的语言解析器,这样便可以省却了自己全手工打造的劳苦。它是这样的一种工具,它可以接受文法语言描述,并能产生识别这些语言的语句的程序。作为翻译程序的一部分,你可以使用简单的操作符和动作来参数化你的文法,使之告诉ANTLR怎样去创建抽象语法树(AST)和怎样产生输出。ANTLR知道怎样去生成识别程序,语言包括Java,C++,C*和不久的Python。ANTLR知道怎样构建识别程序,这些程序可以对以下三种不同的输入应用文法结构:(i)字符流,(ii)记号流,和(iii)两维的树结构。很自然的它们分别与词法分析程序(le*ers,以下简称le*er),语言解析程序和树遍历程序向匹配。这个用于定义这些语法的元语言,在所有情况下几乎一样的。一旦你对ANTLR和类似工具比较顺手,你会开始以一种新的目光来看编程。许多任务强烈需要语言解决方案,而不是采用传统编程语言的做法。比如,这些过程的注解都是用特伦斯标记语言写的。而ANTLR则能来将文本(含一些额外的东西和转换)转化为HTML,PDF或者其他那些生成程序的文件格式。最后,ANTLR只是一件工具,仅仅这些。虽然它能通过将容易理解的乏味部分自动化来帮助你创建软件,但却不能企图让你指定整个编译器。例如,在单个的描述里就不行。那些宣称这类事情非常伟大,可以为发布刊物文章编写惊人的“一揽子解决方案”,却会悲惨失败在实际项目中。词法分析器(Le*er)词法分析器又称为Scanner,Le*icalanalyser和Tokenizer。程序设计语言通常由关键字和严格定义的语法结构组成。编译的最终目的是将程序设计语言的高层指令翻译成物力机器或虚拟机可以执行的指令。此法分析器的工作是分析量化那些本来毫无意义的字符流,将他们翻译成离散的字符组(也就是一个一个的Token)括关键字,标识符,符号(symbols)和操作符供语法分析器使用。语法分析器(Parser)编译器又称为Syntacticalanalyser。在分析字符流的时候,Le*er不关心所生成的单个Token的语法意义及其与上下文之间的关系,而这就是Parser的工作。语法分析器将收到的Tokens组织起来,并转换成为目标语言语法定义所允许的序列。无论是Le*er还是Parser都是一种识别器,Le*er是字符序列识别器而Parser是Token序列识别器。他们在本质上是类似的东西,而只是在分工上有所不同而已。ANTLRANTLR将上述两者结合起来,它允许我们定义识别字符流的词法规则和用于解释Token流的词法分析规则。然后,ANTLR将根据用户提供的语法文件自动生成相应的词法/语法分析器。用户可以利用他们将输入的文本进行编译,并转换成其他形式(如AST—AbstractSynta*Tree,抽象的语法树)。Antlr使用安装及使用到./下载最新版本的ANTLR开发包和源码(例如版本3.01)。将antlr-.jar所在目录配置到你的环境变量中,写好语法文件(例如SensorSQL.g),运行命令“javaantlr.ToolSensorSQL.g”就可以获得自动生成语法/词法分析器。ANTLR语法文件解析下面我们对图中所描述的ANTLR语法文件做一些详细的分析。为了更好的使用ANTLR,你还可以下载ANTLR的Eclipse插件来帮助你完成工作。1.header域:所有出现在这里的部分,都会出现在由ANTLR编译之后生成的Java文件的最顶部。在本例中你可以将包名和其他信息放到这一区域中,生成的结果如由面对应代码部分所示。2.你在这一部分所提供的容对于文件中的每个语法都是唯一的。这一区域的容将出现在实际的类定义之前。也就是说,两个import仅属于类CalcParser,而不属于在同一个文件中定义的其他类(如CalcLe*er)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:正如你所看到的那样,我们可以在一个规则定义中作与一个函数等价的所有事情。我们可以为规则指定参数(像上面的inta),制定返回值(intc),甚至抛出一个异常。从右半面我们可以清楚地看到,所有在规则中定义的容都被忠实而准确的翻译到Java源文件的相应位置。5:这一可选的部分为我们提供了指定*些可选参数的能力。例如图中所示代表告诉ANTLR在生成代码的时候不要生成缺省的错误处理部分,这部分将由用户自己负责。7:在异常处理部分,我们可以指定自定义的异常处理方法。像这里就仅仅是打印错误栈信息。ANTLR语法实例—SensorSQLSensorSQL是一个自定义的简化版SQL语言,它所支持的语法定义这里就不详细列出了,我只是给出查询的示例:通常,编译一个查询的目的是要把它转化成*种被查询设备可以理解的形式。通常的做法有两种,一种是像在上一节中提到的那样,写好详细的语法规则,在ANTLR生成相应的Java文件之后,就可以直接使用其运行结果。这样的例子有很多,其中最典型的就是对于算数表达式的解析了。对于形如1+2-3*4/5^6这样的表达式,只要写好语法规则,就可以在解析过程中直接得到运算结果:首先ANTLR将其编译成逆波兰结构--(-(+12)(/(*34)(^56)));在生成语法树的过程中,同步计算表达式的值,即类似于2.3节中看到的表达式计算。结果如下:不过这样作有一个缺点,就是在很多情况下,你可能并不知道要用什么样的方法来处理。所以当真正要开始写处理代码的时候,就要受限于已有的Parser/Le*er中的代码。一旦要有所修改,就要重新编译语法文件,生成新的Java代码,不胜繁琐。而且,一旦处理过程有误,就要反复调试修改Antlr生成的代。自动生成的代码嘛,结构着实也不怎么样,调试的时候也麻烦。所以如果效率允许的话,就没有必要让Antlr作额外的工作,干脆就专心于做他的语法分析也就是了,其他的工作等到生成语法树之后再怎么遍历或者折腾都可以嘛J。上图就是刚才演示的SensorSQL语法分析之后产生的结果。在产生这个结果之后,我需要将每一个语法元素翻译成字节序列打包发送给传感器网络。这时候,为了保证Where语句中的优先级,你就可以按照ANTLR文档中关于生成语法树的一章,生成类似于这样的结构,然后只需前序遍历这颗语法树的Where部分就可以达到目的,至于其他部分,顺序遍历一遍就好了。ANTLRStudio有了前面的基础之后,我们就可以开始真正的工作了。不过用“记事本或Editplus+命令行”或者干脆写个ANT脚本也不是不可以,但是总觉得在集成化IDE满天飞的时代用这个方式有点过于原始,幸好PlacidSystem为我们提供了一个Eclipse插件来使我们有机会直接走出原始社会。下载地址为:.placidsystems./,目前最新版本是1.1.0。唯一令人遗憾的是这个插件虽然功能很完善,却是要收费的,否则只有11天的试用期。ANTLRStudio插件的安装Eclipse下插件的安装自不必多说,要注意的是从PlacidSystem上提供的license文件,下载之后它的名字为license.lic.t*t,要把它的后缀名.t*t去掉,然后放到ECLIPSE_DIR\plugins\AntlrStudio_*.*.*目录(这里*.*.*是版本号,例如-1.1.0)。安装成功之后在Eclipse的工具栏上会出现一个词法分析器的导航按钮:当右键单点击你的工程时,你会发现控制是否使用ANTLRStudio的开关:当打开一个文法文件之后,可以看到如下界面:在右面的大纲窗口,列有所有Parser和Le*er的元素,可以看到ProtectedToken(例如Number)和其他普通的Token是不一样的;在左面,不同的区域是用不同的颜色块加亮来区分的。功能介绍ANTLRStudio在EclipseHelp提供了比较详尽的文档描述,所以这里我只介绍一些1.1.0版本的新功能。l完全支持ANTLR,并支持将之前的工程自动升级到1.1.0版本。lSynta*DiagramView,可以方便的查看所输入的语法结构。l改进了Debug功能,可以调试比较大的文法文件。而在这之前,如果一个文法文件很大的话,ANTLRStudio就会抛出异常。l支持自动的代码补全功能,提供一个ANTLR文档的比较全面的提示信息(如下所示)。语法图表视图(Synta*DiagramView)在Window->ShowView->Other中选择显示这个视图之后,你就可以使用这个很酷的功能了利用这个视图,你可以很容易的看到你定义语法的语法结构,例如,我的SELECT语句定义如下你只需要将光标标放到selectStatement规则的任意位置,就可以在Synta*DiagramView中看到:于是完整的语法结构清晰的显示在了我们面前。这时你只需要将光标放到脱字符号(^)上面(注:脱字符号用于指明在生成语法树的时候,脱字符号所在的SubRule要作为树或子树的根节点):就会看到:对应的SubRule被加亮成粉红色,而如果你的光标放到的位置是一个Token的话就会变成淡蓝色,简直太酷了。增强的Debug功能想要启动或关闭ANTLRStudio的Debug功能,需要完成以下步骤:l在工程中启用/取消ANTLRStudiol右键单击工程,打开“属性”中的ANTLRStudio选项卡。l选择/取消'Enabledebuggingingrammarfiles'
做完这些后,我们就可以痛快的使用其Debug功能了。与调试其他Java文件一样,我们可以在语法文件的任意位置插入断点:当程序运行至断点之后,我们同样可以像调试普通应用程序一样使用诸如“跳过”,“继续”等Java应用程序的Debug方式来进行,十分的方便和顺手。注:以上资料均来于网络,鄙人收集整理。ANTLR中文手册06通讯软件06382027毅本文主要概括了一些常用的ANTLR的使用方法,其中有Antlr的主要类,Antlr文法文件形式,生成Java类,如何生成Java类,如何执行以及元语言词汇表。一、Antlr的主要类:Antlr中有主要类有两种(其实还有一种TreeLe*er)
Le*er:文法分析器类。主要用于把读入的字节流根据规则分段。既把长面条根据你要的尺寸切成一段一段:)并不对其作任何修改。
Parser:解析器类。主要用于处理经过Le*er处理后的各段。一些具体的操作都在这里。
二、Antlr文法文件形式:
Antlr文件是*.g形式,即以g为后缀名。
例如:t.g
classPe*tendsParser;
startRule
:n:NAME
{System.out.println("Hithere,"n.getTe*t());}
;
classLe*tendsLe*er;
//one-or-morelettersfollowedbyanewline
NAME:(’a’……’z’|’A’……’Z’)NEWLINE
;
NEWLINE
:’\r’’\n’//DOS
|’\n’//UNI*
;
具体成分分析:
1、总体结构
ClassPe*tendsParser
ClassLe*tendsLe*er
两行同JAVA继承一样,P继承Parser类;L继承Le*er类。每个.g文件只能各有一个。
2、Le*er类分析
一般按照类型名:(匹配的具体规则)的形式构成。是分隔字节流的依据。同时可以看到里面可以互相引用。如本例中的类型名NEWLINE出现在NEW的匹配规则中。
3、Parser类分析
一般按照
起始规则名:
规则实例名:类型名或规则名
{Java语句……;}
;
起始规则名:任意。
规则实例名:就象Java中“Strings;”的s一样。规则实例名用于在之后的JAVA语句中调用。
类型名或规则名:可以是在Le*er中定义的类型名,也可以是Parser中定义的规则名。感觉就像是int与Integer的区别。
Java语句:指当满足当前规则时所执行的语句。Antlr会自动嵌入生成的java类中。三、生成Java类
1、从.上下载antlr-*.*.*.jar
2、配置环境变量:classpath=.;*:\jdk\lib\tools.jar;*:\antlr-*.*.*.jar
3、在t.g所在目录下执行:
javaantlr.Toolt.g
会在当前目录下生成如下文件:
L.java:Le*er文法分析器java类。
P.java:Parser解析器java类。
PTokenTypes.java:Le*er中定义的类型具体化,供Parser解析器调用。
PTokenTypes.t*t:当外部的(如t2.g)要调用当前的类型或规则时要用到本文件。四、执行
1、编写Main类
importjava.io.*;
classMain{
publicstaticvoidmain(String[]args){
try{
Lle*er=newL(newDataInputStream(System.in));
Pparser=newP(le*er);parser.startRule();
}catch(E*ceptione){
System.err.println("e*ception:"e);
}
2、执行
c:\>javac*.java
c:\>javaMain
Terence
^Z
Hithere,Terence
c:\>元语言词汇表空格定义空格,tab符号和换行符号在ANTLR分隔诸如标识符这样的词汇符号时作为分隔符。在这之外,它们是被忽略的。例如,“FirstNameLastName”对ANTLR来说两个标记符而不是一个标记符,空格,然后再接着一个标记符。注释ANTLR接受C语言风格的块注释和C++风格的行注释。在语法类和规则中,Java风格的文档注释也是可以接受的,在需要的时候,这些注释可以被传递给生成的输出文件。例如/**Thisgrammarrecognizessimplee*pressions*authorTerenceParr*/classE*prParser;/**Matchafactor*/factor:...;字符集字符常数像Java中那样被确定。它们包含八进制转义字符集(e.g.,'\377'),Unicode字符集(e.g.,'\uFF00'),和能被Java识别的常用的字符转义('\b','\r','\t','\n','\f','\'','\\')。在词法分析器规则中,单引号代表一个可以在输入字符流中能得到匹配的的字符。在语法分析器中是不被支持单引号的字符的。文件结束标志EOF标记用语法分析器规则中自动生成:rule:(statement)+EOF;你可以在词法分析器规则的动作中检测EOF_CHAR符号://makesurenothingbutnewlineor//EOFispastthe*endifENDIF{booleaneol=false;}:"*endif"(('\n'|'\r'){eol=true;})"{if(!eol){if(LA(1)==EOF_CHAR){error("EOF");}else{error("Invalidchars");}}};当你将文件结束当一个字符来检测时,它实际上并不是一个字符,而是一个条件。你可以在你的词法分析器语法中覆盖
CharScanner.uponEOF()函数:/**ThismethodiscalledbyYourLe*er.ne*tToken()*whenthele*erhas*hitEOFcondition.EOFisNOTacharacter.*ThismethodisnotcalledifEOFisreached*duringsyntacticpredicateevaluationorduring*evaluationofnormalle*icalrules,which*presumablywouldbeanIOE*ception.This*trapsthe"normal"EOF*condition.**uponEOF()iscalledafterthepleteevaluation*oftheprevioustokenandonlyifyourparserasks*foranothertokenbeyondthatlastnon-EOFtoken.**Youmightwanttothrowtokenorcharstream*e*ceptionslike:"Heh,prematureeof"oraretry*streame*ception("Ifoundtheendofthisfile,*gobacktoreferencingfile").*/publicvoiduponEOF()throwsTokenStreamE*ception,CharStreamE*ception{}文件结束条件是一个位比特。因为Terence将-1当作一个字符而不是一个整型数。(-1是'\uFFFF').字符串字符串常数一个由双引号括起来的一系列字符。在字符串中的字符可以是作为字符也同样合法的转义字符(八进制,Unicode等)。当前,ANTLR实际上不允许Unicode出现在字符串常量中(你不得不用转义符)。这是因为在anglr.g文件中设定charVocabulary选项为ascii.在词法分析器规则中,字符串被解释成可以在输入流中匹配的一系列字符(例如.,"for"等于'f''o''r').在语法分析器规则中,字符串代表一个个标记(tokens),并且每个独立的字符串被分派一个标记类型。然而,ANTLR不会创建一个词法分析器规则来匹配这些字符串。相反,ANTLR将这些字符串输入到一个于词法分析器关联的字符常量表中。ANTLR将针对字符常量表来产生代码检测每个标记中的文本,在手动关掉语法分析器对该标记的处理之前获得一个匹配时,会改变标记的类型。你也可以执行手动检测――自动代码生成可以通过词法分析器选项控制。你可能想在你的动作中使用这些字符串常量的标记的类型值,例如在错误处理器的同步部分。对于只由字母字符组成的字符串常量来说,这个字符串常量的值将是一个形如LITERAL_***的常量值,这里***是这个标记的名字。例如,文字“return”将有一个LITERAL_return值与之关联。你也可以用标记节(tokenssection)分派一个特定的标号给这个文字。标记引用以大写字符开头的标识符称为标记引用。接下来的字符可以是任何字符,数字或下划线。在语法分析器规则中一个标记引用将导致匹配特定的标记。在词法分析器中的标记引用将导致调用一个词法规则来匹配该标记的字符。换句话说,在词法分析器中的标记引用将对当作一个规则引用。标记定义在词法分析器中的标记定义由和语法规则中相同的定义。但是当做标记而不是语法规则。例如,classMyParsere*tendsParser;idList:(ID)+;//parserruledefinitionclassMyLe*ere*tendsLe*er;ID:('a'..'z')+;//tokendefinition规则引用以小写字母开头的标识符是为ANTLR的语法规则。接下来的字符可以是任意字母,数字或下划线。词法规则不能引用语法规则。动作.在尖括号中的字符序列是语义动作(可能是嵌套的)。在字符串和字符中的尖括号不是动作分隔符。动作参数在方括号中的字符序列是动作参数(可能是嵌套的)。在字符串和字符中的方括号不是动作分隔符。在[]中的参数是用被生成的语言的语法定义的,并且用逗号分开。codeBlock[intscope,Stringname]//inputargumentsreturns[int*]//returnvalues:...;//pass2args,getreturntestcblock{inty;} : y=cblock[1,"John"] ;许多人喜欢我们用普通的括号来括住参数,但是括号在EBNF中已经被很好的用来定义语法组符号(grammaticalgroupingsymbols)。符号下面的表统计了在ANTLR中使用的标点符号和关键字。符号描述(...)子规则(...)*闭包子规则(零和多个)(...)+正闭包子规则(一个和多个)(...)"可选(零个和一个){...}语义动作[...]规则参数{...}"语义谓词(...)=>语法谓词|可选符..围符~非.通配符=赋值:标号符,规则开始;规则结束<...>元素选项class语法类e*tends指定语法基类returns指定返回类型optionsoptions节tokenstokens节headerheader节tokenstoken定义节Header节一个header节包含了一些将直接被替换到输出的语法分析器中的源码,这些源码将在所有的ANTLR生成的代码之前。这个主要用在C++的输出中,因为C++需要一些元素在引用之前必须被声明。在Java中,这可以用来为最后的语法分析器指定一些包文件。一个header节看起来像下面这样:header{sourcecodeinthelanguagegeneratedbyANTLR;}header节是语法文件的第一个节。根据选择的目标语言的不同,不同类型header节都是可能出现的。看各自的附录。语法分析器类定义所有的语法规则必须和一个语法分析器关联。一个语法文件(.g)只包含一个语法分析器类定义(和词法分析器和树遍历器一起)一个语法分析器定义在它的选项(options)和规则定义之前。一个语法文件中的语法分析器定义通常是这个样子:{optionalclasscodepreamble}classYourParserClasse*tendsParser;optionstokens{optionalactionforinstancevars/methods}parserrules...当在面向对象语言中生成代码时,语法分析器类将导致在输出中是一个类,规则都会变成这个类的成员函数。在C中,类将导致生成一个结构,一些名字混淆(name-mangling)算法将用在上面使最终的规则函数是全局唯一的。可选的类的预定义可以是包含在{}中的任意文本。这个预定义,如果它存在的话,将被直接输出到生成类文件中,并且在类定义之前。封闭的尖括号不能用来分隔类,因为一个左尖括号在文件顶就很难跟踪与之匹配的右括号在文件的时。相反,一个语法分析器类假定是连续的,知道碰到下一个类的语句。你可以指定语法分析器超类,它将作为被生成的语法分析器的超类。这个超类必须是完整定义的,在双引号中。它自己必须是antlr.LlkParser的子类。例如classTinyCParsere*tendsParser("antlr.debug.ParseTreeDebugParser");词法分析器类定义一个语法分析器类将导致一个知道如何根据输入流的标记来应用语法结构的语法分析器对象。为了执行词法分析,你需要指定一个词法分析器类,它描述了如何将输入流分离成标记流。它的语法类似于语法分析器类:{optionalclasscodepreamble}classYourLe*erClasse*tendsLe*er;optionstokens{optionalactionforinstancevars/methods}le*errules...包含在词法分析器中的词法规则在产生的类中变成成员方法。每个语法文件(.g)只包含一个词法分析器。语法分析器和词法分析器可以以任何顺序出现。可选类的开头是括在{}中的任意文本。这个开头部分,如果它存在,将输出到被生成的类文件中,在类定义的之前。你可以定义一个词法分析器的超类,它可以被用来作为产生的词法分析器的超类。这个超类将是完整定义的(fully-qualified),在双引号中,它自身是antlr.CharScanner子类。树分析器定义一个树分析器像语法分析器,不同的是它处理二维的由节点组成的抽象语法树,而不是处理由标记组成的流。树分析器必须唯一指定给语法分析器,除非规则定义中包含特殊形式致使它递归下降到树中。同样,一个特定的语法文件(.g)中仅仅包含一个树分析器。{optionalclasscodepreamble}classYourTreeParserClasse*tendsTreeParser;optionstokens{optionalactionforinstancevars/methods}treeparserrules...你可以定义一个树分析器的超类,它可以被用来作为产生的树解析器的超类。这个超类将是完整定义的(fully-qualified),在双引号中,它自身是antlr.TreeParser子类注:以上资料均来于网络,鄙人收集整理。计算器及文法文件06通讯软件06382027毅本文主要介绍了利用ANTLR达到计算器需求以及利用ANTLR生成C++描述的分析程序。一.利用ANTLR达到计算器需求1.运算符:+,-,×,÷,(,)2.支持整型和浮点型测试样例1.1+2*3+5-4/22.(1+2)*3+5-4/(2+2)3.(1.2*2.5)+8/(4-3)*2.7下面就让我们动手完成一个计算器,:)
先搭个框架。文件名是calc.goptions{
language=“Cpp”;
}classCalcParsere*tendsParser;classCalcLe*ere*tendsLe*er;这些就是基本框架了。
options里设置language为”Cpp”,表示要生成c++代码。
CalcParser是我们的计算器的语法解析类,继承ANTLR里的Parser类。
同理,CalcLe*er是词法分析类,继承ANTLR里的Le*er类。
接着定义计算器的词法规则。首先是运算符。
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:me*pr((PLUS|SUB)me*pr)*
;
me*pr:e*pr((MUL|DIV)e*pr)*
;
e*pr:INT
|REAL
|LPARENstatementRPAREN
;一个计算器的语法程序就写好了。让我们来生成c++代码,实际测试一下.
$java-cp/usr/share/java/antlr.jarantlr.Toolcalc.g
ANTLRParserGeneratorVersion2.7.6(20060528)1989-2005
$ls
calc.gCalcParser.cppCalcParserTokenTypes.t*t
CalcLe*er.cppCalcParser.hpp
CalcLe*er.hppCalcParserTokenTypes.hpp
$
我们可以看到,生成了6个文件。
CalcTest.cpp:*include“CalcLe*er.hpp”
*include“CalcParser.hpp”
*include
usingnamespacestd;
usingnamespaceantlr;intmain()
{
try{
CalcLe*erle*er(cin);
CalcParserparser(le*er);
parser.statement();
}catch(e*ception&e){
cout<<e.what()<<endl;
}
}编译之:
$g++-oCalcCalcTest.cppCalcParser.cppCalcLe*er.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)及规则定义、词法分扫描器类(le*er)及token定义。其中最为重要的是规则和token的定义。规则的定义形式和编译理论中的扩展巴科斯式(EBNF)极为相似,包括规则名、规则体、一个用作结束标志的分号和异常处理部分(可省略)。例如如下的规则就描述了C语言中的赋值语句的语法:assignment_stat:
id'='e*pr';'
;其意义是:一条赋值语句是由一个id、一个等号、一个表达式和一个分号顺序组成的。Token的定义方法与规则类似。例如如下的token定义就表示一个十进制的整数:NUM:
('1'..'9')('0'..'9')*
;其意义是:数字(NUM)的第一字符是‘1’到‘9’中的一个字符,后面是0个或多个‘0’到‘9’之间的字符。需要注意的一点是:规则的名字必须是小写字母开始,而token的名字则必须是大写字母开始。设定ANTLR生成的语言(转自ANTLR中文手册)ANTLR有很多选项,可以通过在文法文件中的options块中进行设置,其中包括ANTLR最终生成的语言。如果要生成C++描述的分析器程序,就要如下设定:options{language="Cpp";//Otheroptions}language选项的默认值是“Java”。如果您希望生成的程序是C*的,将language设为“Csharp”就可以了。C++程序的例子下面就给出一个ANTLR生成的C++描述的分析器的实例。该分析器的功能是分析用户输入的一个算术表达式,给出该表达式的最终结果。在该表达式中允许出现的运算符除了加减乘除之外,还包括求幂运算符“^”,以及sin、cos和tan三个三角函数。编写文法文件header{*include<stdlib.h>*include<stdio.h>*include<math.h>}options{language="Cpp";}classE*prParsere*tendsParser;{}//rulese*prreturns[doublevalue=0]{double*;}:value=term(PLUS*=term{value+=*;}|MINUS*=term{value-=*;})*;e*ceptioncatch[ANTLR_USE_NAMESPACE(antlr)ANTLRE*ception&e*]{//catchalle*ceptionsandreportitreportError(e*.toString());}termreturns[doublevalue=0]{double*;}:value=factor(STAR*=factor{value*=*;}|SLASH*=factor{value/=*;})*;factorreturns[doublevalue=0]{double*;}:value=atom(TOK_POW*=atom{value=pow(value,*);})*;atomreturns[doublevalue=0]{double*;}:i:NUM{value=atof((i->getTe*t()).c_str());}|TOK_SIN*=atom{value=sin(*);}|TOK_COS*=atom{value=cos(*);}|TOK_TAN*=atom{value=tan(*);}|LPARENvalue=e*prRPAREN;e*ceptioncatch[ANTLR_USE_NAMESPACE(antlr)ANTLRE*ception&e*]{reportError(e*.toString());}clas
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024护理安全培训
- 物理因子疗法及康复护理水疗法
- 医护理系彭芳
- 实验室主任安全培训
- 大班语言活动生字表
- 对新员工的财务培训
- 7月珠宝活动策划方案
- 数学学案:课堂导学函数的表示方法第课时分段函数
- 2岁护理方法和技巧
- 健康扶贫培训教材
- 小学低年级自主识字的教学策略
- 第五单元学雷锋在行动(教案)全国通用五年级下册综合实践活动
- 服装店人员不稳定分析报告
- GB 37219-2023充气式游乐设施安全规范
- NB-T 47013.7-2012(JB-T 4730.7) 4730.7 承压设备无损检测 第7部分:目视检测
- 《梯形的认识》(课件)-四年级上册数学人教版
- 肝吸虫护理查房课件
- 北京开放大学《现代管理专题》终结性考试复习题库(附答案)
- 小腿抽筋的原因以及缓解和自救方法定稿
- 2023年度高级会计实务真题及答案解析
- 南开大学答辩通用模板
评论
0/150
提交评论