[转载] ANTLR——语法分析
来源:ANTLR中文网站:http://www.antlr.org.cn
语法分析是编译过程的第二步,在词法分析提供的记号流的基础上,对源代码的结构做总体的分析。无论分析的内容有多大语法分析总是由一个启始规则开始的,最后总是生成一棵语法树。一般情况语法规则是一个文法的主体部分,也是编写文法的难点。本章用几个示例来讲述如何用ANTLR定义语法规则。
4.1语法分析的方法
在ANTLR中语法分析定义的规则名必须以小写字母开始大写如“baseClass”,“subfixSymbol”。如果词法规则与语法规则写在同一个文件时,虽然ANTLR中并没有严格定义规则的先后顺序,但一般情况下语法规则写到词法规则的上面,因为整个文法的启始规则是从语法规则开始的,这样可以从上到下查看整个文法。
ANTLR中语法定义的方法与词法基本相同请看下面一个SQL文法的片段示例:
grammar Test;
sqlStatement : selectStatement | insertStatement | deleteStatement;
selectStatement : SELECT (ALL | DISTINCT)? SelectList FROM tableSource;
SelectList : SelectItem+;
tableSource : TableName | '(' selectStatement ') ';
4.2递归定义
定义文法时通常出现递归的情况,比如上例中tableSource中的子查询就是一个递归定义。在C++,C#,java语言中类名这样的符号是用“.”分隔的多个标识符组成的,如java.IO,System.Web.UI等这种情况需要使用递归的方法来定义,递归有左递归和右递归。
左递归:
qualifiedName : qualifiedName '. ' Identifier;
qualifiedName : Identifier;
Identifier : ('a'.. 'z' | 'A'.. 'Z' | '_') ('a'.. 'z' | 'A'.. 'Z' | '_' | '0'.. '9')*;
右递归:
qualifiedName : Identifier '.' QualifiedName
qualifiedName : Identifier;
Identifier : ('a'.. 'z' | 'A'.. 'Z' | '_') ('a'.. 'z' | 'A'.. 'Z' | '_'| '0'.. '9')*;
不过在ANTLR中不允许左递归定义,ANTLR会提示:“rule is left-recursive”错误。ANTLR中还有别一种定义方法,象类名这样的符号递归定义使用这种方法是最好的方案。原因我们会在后面章节讲述。
qualifiedName : Identifier ('.' Identifier)*;
Identifier : ('a'.. 'z' | 'A'.. 'Z' | '_') ('a'.. 'z' | 'A'.. 'Z' | '_' | '0'.. '9')*;
4.3 java方法的文法示例
下面来看一个定义java方法的文法,些文法是从ANTLR自带的java.g示例中选出的一段:
methodDeclaration :type Identifier
(('(' (variableModifier* type formalParameterDeclsRest?)? ')')
('[' ']')*
('throws' qualifiedNameList)?
( methodBody
| ';'
)) ;
formalParameterDeclsRest :
variableDeclaratorId
(',' (variableModifier* type formalParameterDeclsRest?))?
| '...' variableDeclaratorId
;
type : Identifier (typeArguments)?
('.' Identifier (typeArguments)? )* ('[' ']')*
| primitiveType ('[' ']')*
;
variableModifier : 'final' | annotation ;
formalParameterDeclsRest :
variableDeclaratorId
(',' (variableModifier* type formalParameterDeclsRest?))?
| '...' variableDeclaratorId
;
variableDeclaratorId : Identifier ('[' ']')* ;
Identifier : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*;
typeArguments : '<' typeArgument (',' typeArgument)* '>' ;
typeArgument : type
| '?' (('extends' | 'super') type)? ;
qualifiedNameList :qualifiedName (',' qualifiedName)* ;
qualifiedName :Identifier ('.' Identifier)* ;
methodDeclaration为方法定义的起始规则,后面type Identifier为方法的返回值和方法名,( '(' (variableModifier* type formalParameterDeclsRest?)? ')' ) ('[' ']')*中的variableModifier为参数前的标识符,type为参数的类型formalParameterDeclsRest是对参数其余部分的定义,参数表在“()”中,后面是“[]”字符,这是数组类型为返回值时需要的,因为数据可以是多维的后是用('[' ']')*来定义。
formalParameterDeclsRest用来定义参数表部分,variableDeclaratorId为参数名,如何有一个以上参数(',' (variableModifier* type formalParameterDeclsRest?))?部分字义了第二个到第N个参数的规则,这里使用了右递归方法。Java1.5中支持可变参数 '...' variableDeclaratorId为可变参数的定义。
另外typeArguments配合Identifier来定义泛型类型,使用“<>”括起来一个类型列表,如List<string>。typeArgument与type形成了递归定义,如Hashtable<string,List<stirng>>。
4.4 | 作用范围
上例中的typeArgument : type | '?' (('extends' | 'super') type)? 规则无须写成typeArgument:type | ('?' (('extends' | 'super') type)?)。在同一规则或子规则中“|”使其两侧的内容为并列的选择关系,如果有多个“|”则一起为并列的选择关系,需要改变优先顺序时才使用“()”。
4.5 SELECT语句文法示例
下面给出一个SQL SELECT语句文法片段的例子。
grammar Select;
statement
: selectStatement (SEMICOLON)?;
selectStatement
: queryExpression (computeClause)? (forClause)? (optionClause)?;
queryExpression
: subQueryExpression (unionOperator subQueryExpression)* (orderByClause)?
;
subQueryExpression
: querySpecification | ‘(‘ queryExpression ‘)’
;
querySpecification
: selectClause (fromClause)? (whereClause)? (groupByClause (havingClause)? )?
;
selectClause
: SELECT (ALL | DISTINCT)? (TOP Integer (PERCENT)?)? selectList
;
whereClause
: WHERE searchCondition
;
orderByClause
: ORDER BY expression (ASC | DESC)? (COMMA expression (ASC | DESC)? )*
;
groupByClause
:GROUP BY (ALL)? expression (COMMA expression)* (WITH (CUBE | ROLLUP) )?
;
havingClause
: HAVING searchCondition
;
SEMICOLON : ';';
我们对SELECT语句都比较熟悉,看一下SELECT语句文法的大体结构。selectStatement规则表示整个SELECT语句体系,queryExpression表示查询语句可能由多个子查询用UNION到一起unionOperator代表UNION或UNION ALL关键字,orderByClause子句出现在SELECT语句的最后,这里subQueryExpression和queryExpression之间形成了递归定义,子查询中还可以有子查询。whereClause子句和havingClause子句后面都是查询过滤表达式后成它们共用searchCondition规则。
4.6 HTML文法示例
下面再看一个HTML文法片段示例:
grammar HTML;
options {language=CSharp; output=AST;}
document : OHTML body CHTML;
body : obody (body_content)* CBODY ;
body_content : body_tag | text;
body_tag : block; // | heading | ADDRESS
text : text_tag;
text_tag : form;// phrase | special | font
block : table;//paragraph | list | preformatted | div | center | blockquote | HR |
table : otable (tr)+ CTABLE;
tr : o_tr (th_or_td)* (C_TR)? ;
th_or_td : o_th_or_td (body_content)* (C_TH_OR_TD )?;
form : oform (form_field | body_content)* CFORM;//
oform : '<form' (ATTR)* '>';
form_field : inputField;// | select | textarea;
inputField : '<input' (ATTR)* '>';
obody : '<body' (ATTR)* '>';
CBODY : '</body';
OHTML : '<html>';
CHTML : '</html>';
otable : '<table' (ATTR)* '>';
CTABLE : '</table>';
o_tr : '<tr' (ATTR)* '>';
C_TR : '</tr>';
o_th_or_td : ('<th' | '<td') (ATTR)* '>';
C_TH_OR_TD : '</th>' | '</td>';
CFORM : '</form>';
ATTR : WORD ('=' (WORD ('%')? | ('-')? INT | STRING | HEXNUM))?;
HEXNUM : '#' ('0'..'9' | 'a'..'f')+;
INT : (DIGIT)+;
DIGIT : '0'..'9';
WORD : (LCLETTER | '.' | '/') (LCLETTER | DIGIT | '.')+;
STRING : '"' (~'"')* '"' | '\'' (~'\'')* '\'';
LCLETTER : 'a'..'z';
WS : ( ' ' | '\t' | '\n' | '\r' ) + {Skip();} ;
这个可运行的简化的HTML文法中只支持<form>表单和<table>表格。O开头的符号如OHTML、otable表示标记开头<html>和<table>,C开头的符号是表示结束标记如:CHTML,CTABLE表示</html>和</table>。document规则表示整个HTML文档其包括body部分。Body主要包括两种内容text和body_tag,body_tag中包括<table>表格,text中包含<form>表单。词法规则可以和语法规则混合书写这是允许的。
文法中的th_or_td规则表示表格中的一个cell,其中又包含了body_content这个递归的定义使得表格中的第一个小格都可以嵌套的包含HTML标记。生成分析器后可以对下面的HTML文件进行分析。
<html>
<body id="doc">
<table>
<tr>
<td>
<form action="login.jsp" >
<input id="txt1" value="" />
</td>
</tr>
</table>
</form>
</body>
</html>
4.7 Skip()的效果
在HTML文法示例中的开始标记的定义是用语法规则定义的,而结束是用词法规则定义的ANTLR中规则可以灵活定义不拘一格,结束标记只是固定的字符串定义成词法规则就可以了。而开始标记中要包含属性的定义所以用语法规则来定义。其中有一点要注意的是如果我们把开始标记也定义成词法规则会出现什么情况呢?如下面改变一下HTML文法。
OFORM : ‘<form’ (ATTR)* ‘>’
将<form>表单的开始标记改为词法规则,这时分析器不能正确分析上面的html文件。在分析<form ation=””>行时异常。运行这个示例的语句如下:
HTMLLexer lex = new HTMLLexer(new ANTLRFileStream("t.html"));
ITokenStream tokens = new CommonTokenStream(lex);
HTMLParser parser = new HTMLParser(tokens);
HTMLParser.document_return dReturn = parser.document();
这是因为空白规则WS中的Skip()语句的效果是对语法分析而言的。在词法分析阶段Skip()并没有去掉空格,我们可以java和.net的开发环境中查看tokens对象了解词法分析阶段的结果,这可以帮助我们分析问题。对于输入的“<form ation="">”来说 form和ation之间有一个空格这时词法分析器会失败,因为我们在这个规则中没有定义空白,除非我们这样写这个词法规则。
OFORM : ‘<form’ WS (ATTR)* WS? ‘>’
把所有可能出现空格地方都加下显示地定义空白才行。这也就是我们在上一章中的FuzzyJava2示例中为什么词法规则中加了很多WS的原因。
4.7 语法规则中的字符常量
可能读者早已发现我们在语法规则中直接写入了需要匹配的字符。如:
oform : '<form' (ATTR)* '>';
‘<form’和‘>’都属于词法范畴,ANTLR为了使书写简单直观,允许在语法规则中直接写出需要匹配字符。在生成分析器代码时这些常量会自动被放入词法分析程序中,语法分析程序中的字符串会用生成的序号代替。oform : '<form' (ATTR)* '>'与下面的写法是等价的。
oform : t10 (ATTR)* t11;
t10 :‘<form';
t11 : ‘>';
4.8 C-语言示例
C-语言是C语言的一个子集,是ANTLR的一个经典示例。下面看一下它的文法。
grammar CMinus;
program
: declaration+
;
declaration
: variable | function
;
variable
: type ID ';'
;
type: 'int' | 'char'
;
function
: type ID
'(' ( formalParameter (',' formalParameter)* )? ')'
block
;
formalParameter
: type ID
;
block
: '{' variable* stat* '}'
;
stat
: forStat | ifStat | expr ';' | block | assignStat ';' | ';'
;
ifStat
: ‘if’ ‘(‘ expr ‘)’ stat (‘else’ stat)?
;
forStat
: 'for' '(' assignStat ';' expr ';' assignStat ')' block
;
assignStat
: ID '=' expr
;
expr: condExpr ;
condExpr
: aexpr ( ('==' | '!=' ) aexpr )?
;
aexpr
: mexpr ('+' mexpr)*
;
mexpr
: atom ('*' atom)*
;
atom: ID
| INT
| '(' expr ')'
;
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* ;
INT : ('0'..'9')+ ;
WS : ( ' ' | '\t' | '\r' | '\n' )+ { $channel = HIDDEN; } ;
启始规则program 表示整个C-程序,declaration表示语言中的声明项,有函数和变量两种声明variable和function。变量声明由类型type加标识符ID组成,function函数声明是由返回类型type函数名ID和参数表'(' ( formalParameter (',' formalParameter)* )? ')',block为函数体。函数体内可以有变量variable和语句stat。
语句stat包括for语句forStat、表达式expr、语句块block和赋值语句assignStat。表达式expr和第一章中的HelloWorld示例类似推导顺序与操作符优先顺序同反,在表达式的末端规则atom有可出现标识符ID、整数INT和嵌套的子表达式,语句块block与函数体block是同一规则,这是一个递归定义使得语句块可以嵌套书写。
生成分析器代码编译运行,来分析如下的C-代码。
char c;
int x;
int foo(int y, char d) {
int i;
for (i=0; i!=3; i=i+1) {
x=3;
y=5;
}
if(5 == 4)
if(5 == 4)
i = 1;
else
i = 2;
}
4.9小结
本章讲述了ANTLR的语法分析,语法分析中的递归定义的应用,用SQL、java、HTML和C-几个实际的例子来让读者对语法分析有更好的理解。通过对Skip()效果的示例说明了词法部分的操作与语法分析的关系。
转载于:https://www.cnblogs.com/6DAN_HUST/archive/2009/01/03/1367523.html
[转载] ANTLR——语法分析相关推荐
- [转载] ANTLR——词法分析
来源:ANTLR中文网站:http://www.antlr.org.cn 词法分析是编译过程的第一步,是编译过程的基础.词法分析除了上一章讲过它为语法分析提拱记号流,滤掉编译过程不关心的内容以外,还有 ...
- mysql词法分析antlr4_[转载] ANTLR——词法分析
词法分析是编译过程的第一步,是编译过程的基础.词法分析除了上一章讲过它为语法分析提拱记号流,滤掉编译过程不关心的内容以外,还有一个重要的作用是有了词法分析可以大大提高编译的效率.可能有人曾有过疑问,为 ...
- [转载] ANTLR——编译原理基础知识
来源:ANTLR中文网站:http://www.antlr.org.cn 编译是将计算机高级语言如C++.Java.C#编写的源程序翻译成可以在计算机上执行的机器语言的翻译过程.编译过程中分:词法分析 ...
- [转载] ANTLR——嵌入文法的Actions
来源:ANTLR中文网站:http://www.antlr.org.cn/ 在ANTLR中词法规则和语法规则都是一些上下文无关的规则,它们不能满足语法分析中的一些高级需求或特殊需求.如:我们可能要判断 ...
- c语言将注释和语句分离,C语言组卷系统中重复题问题研究
C语言组卷系统中重复题问题研究 作者:陈星 李郴 来源:电脑知识与技术 201801期 时间:2018-06-02 摘要:在一套试卷中,重复题问题是影响考试质量的一个重要因素.该文针對C语言试卷中选择 ...
- 学习C++,知识点太多记不住怎么办?
学习一门新的编程语言之前,你首先会做什么? 先熟悉变量.运算符.函数.类和结构.测试等工具,随后按照指导按部就班进行操作? 这样的流程对于学习其他语言如Python.Java来说或许行得通,但C++的 ...
- 我是小白一个,如何快速学会C++?
C++这门语言从诞生到今天已经经历了将近30个年头.不可否认,它的学习难度都比其它 语言较高.而它的学习难度,主要来自于它的复杂性. C++ 现在C++的使用范围比以前已经少了很多,Java.C#.P ...
- 小白如何快速学会C++?
C++这门语言从诞生到今天已经经历了将近30个年头.不可否认,它的学习难度都比其它 语言较高.而它的学习难度,主要来自于它的复杂性.现在C++的使用范围比以前已经少了很多,java.C#.python ...
- 前言-阅读建议和说明
适用范围 该专栏适合对语法感兴趣的小伙伴,希望能从计算机语言语法解析本身的角度去理解一门语言,或者是对编译器前端感兴趣,希望能过编写自己的脚本语言的小伙伴,或者是对当前IDE插件有些不满,希望能够编写 ...
- 使用ANTLR进行语法分析
ANTLR(ANother Tool for Language Recognition)是一款强大的语法分析器生成工具,可用于读取.处理.执行和翻译结构化的文本或二进制文件 传统行业人员通过DSL(D ...
最新文章
- Andrew Gelman、Aki Vehtari​ | 过去50年最重要的统计学思想是什么?
- 使用访问器属性模拟java中的私有变量
- 等参元八节点matlab,四边形八节点等参元matlab程序
- 云信小课堂|5分钟快速实现安卓端PK连麦场景
- spring控制事务:声明式事务(XML)事务的传播行为
- 由装饰者模式来深入理解Java I/O整体框架
- MATLAB--黄金分割法
- stm32f10x 安装包_Keil5 Pack Installer下载_Keil5 Pack 离线安装包下载 2.2.0 官方正式版_当载软件站...
- oCam(屏幕录制) 一款小巧方便的专业屏幕录制软件 彻底解决你的录屏问题
- vep文件如何转换mp4_vep文件如何转换mp4?vep转mp4的操作演示简单又小白
- 新拓三维测量仪器助力土木工程与高端制造迈向数字化
- linux查找文件夹命令
- 手机开发APP整体界面设计工具之墨刀---没用过就知道它很牛掰
- 计算机打印机副机无法打印,打印机共享无法打印怎么办,教您解决电脑打印机共享无法打印...
- word中表格出现无法自动换页问题
- 2021长安“战疫”网络安全卫士守护赛 misc部分writeup
- 电脑安装linux系统需要下载什么软件,教你如何在Linux中安装应用软件
- 鲁棒性(Robustness)
- Github授权登录流程
- eassy--休假三天后决定再次写更新blog
热门文章
- 阶段3 2.Spring_06.Spring的新注解_4 spring的新注解-Import
- 我的Android进阶之旅------gt;Android嵌入图像InsetDrawable的使用方法
- 在Sql Server上安装插件Sql Prompt
- 5天学会jaxws-webservice编程第一天
- SSAS事实表和维度表数据类型必须一致
- java获取文件后缀名(正则表达式)+文件名
- Jquery 强大的表单验证操作
- JAVA-初步认识-第十章-多态-类型判断-instanceof
- string类常用方法3
- 20145235 《Java程序设计》第6周学习总结