语法分析是编译过程的第二步,在词法分析提供的记号流的基础上,对源代码的结构做总体的分析。无论分析的内容有多大语法分析总是由一个启始规则开始
的,最后总是生成一棵语法树。一般情况语法规则是一个文法的主体部分,也是编写文法的难点。本章用几个示例来讲述如何用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