五分钟介绍ANTLR 3

翻译Antlr的内容来学习Antlr,还会让我不会睡觉,不觉得困。

以一个简单的计算器开始,当作学习Antlr的一个例子。

任何一个语言处理程序都有至少两部分:

1、一个词法解释器:获得字符串流,将这个流按预先设定要的规则分割成一个个的token

2、一个语法分析器:读token,根据规则翻译他们。

让我们为一个简单的算数表达式定义一个规则:

grammar SimpleCalc;

add : NUMBER PLUS NUMBER;

NUMBER : ('0'..'9')+ ;

PLUS      : '+';

这个例子包含两部分规则:NUMBER and PLUS ,和一个语法规则 add。词法规则常常以大写字母开头,语法规则以小写字母开头。

  • NUMBER 定义了包含0到9的字符的token,这些字符可以重复一次或一次以上
  • PLUS 定义了一个简单的字符token:+
  • add 定义了一个语法规则,这个语法表示希望接收一个NUMBER token,一个PLUS token,一个NUMBER token,并顺序固定。如果以不同的顺序将会触发错误消息。

分析这个计算

让我们接受更复杂的表达式,像1或1+2或1+2+3+4,这些表达式以一个数字开头,然后加上一个加法标志和一个数(有可能多于一次):

add : NUMBER (PLUS NUMBER)*

*表示0或多次

如果你既想实现加法又想实现减法,你可以做一个小的调整:

add : NUMBER ((PLUS | MINUS) NUMBER)*
MINUS : '-';

你可能已经猜到了,| 意味着“或者”,也就是“加”或者“减”。

如果你希望语法分析完成算术表达式像1+2*3,有一个标准的递归方法来实现它:

expr         : term ( ( PLUS | MINUS ) term )*;
term         : factor ( ( MULT | DIV ) factor )*;
factor       : NUMBER ;

MULT  : '*' ;
DIV   : '/' ;

(计算机一个表达式,通常以expr开头)

我们的语法是不能容忍空格的:当遇到空格,标签符,回车等它会给出警告。我们要告诉词法分析器丢弃任何它所找到的空格是安全的。

首先,我们要定义空格:

  • 一个空格:‘ ’
  • 一个标签符是这样表示的:‘\t’
  • 新的一行是这样表示的:‘\n’
  • 一个回车符是这样表示的:‘r’
  • 一个换页符有一个十进制值12和一个十六进制值0C。Antlr用Unicode编码,所以我们将它定义为4个十六进制数字:‘\u000C’

把这些放在一块,并用一个“or”连接,允许一个或多个一块出现,那么你会得到:

WHITESPACE : ( '\t' | ' ' | '\r' | '\n' | '\u000C' )+;

那么,如果我们写3 + 4*5这个表达式,词法分析器会生成 NUMBER WHITESPACE PLUS WHITESPAC NUMBER MULT NUMBER,而且这将导致语法分析器抱怨未知WHITESPACE标记。我们需要一种方法对于语法分析器,将他们隐藏起来。

 Antlr在词法分析器和语法分析器之间包含两种沟通渠道,默认渠道和隐藏渠道。语法分析器一次只听取一个渠道的内容,所以你可以用将它至于隐藏渠道的方法来隐藏一个标记。

(可以有多于两种渠道而且语法分析器可以单独听取他们中的任一个,也可以从所有合并的渠道获取信息。这在你正在写一个文字处理工具的时候非常有用,这个文字处理工具需要解析出空白和注释的输出,而且让语法分析器忽略这些元素)

对于连续不断的隐藏要求,你用设置这些token的$channel来隐藏他们。这要求你加一些大括号来在词法分析器中添加一点点代码。

WHITESPACE : ( '\t' | ' ' | '\r' | '\n' | '\u000C' ) + { $channel = HIDDEN; };

(如果你现在正在键盘上试图实现词法分析和语法分析的代码,而且正在词法分析器中搜寻 channel = HIDDEN

ANTLR默认生成java代码。你将会在一分钟内学会怎么更改它)

整理你的代码

你可以用很多技术来让你的语法更具有可读性:

  1. 添加注释,比如 // 或者/*....*/
  2. 将你的简单token定义(单字符,单词等)收集到文件顶部的token中
  3. 考虑用fragment规则定义子部分的标记。一个fragment规则将不会自己生成标记,但可以被用作规则的一部分,来定义其余的标记。

这是一个整理好的代码:

 1 grammar SipleCalc;
 2 
 3 tokens{
 4   PLUS     = '+' ;
 5   MINUS    = '-' ;
 6   MULT     = '*';
 7   DIV      = '/';
 8 }
 9 /*------------------------------------------------------------------
10  * PARSER RULES
11  *------------------------------------------------------------------*/
12  
13 expr    : term ( ( PLUS | MINUS )  term )* ;
14  
15 term    : factor ( ( MULT | DIV ) factor )* ;
16  
17 factor  : NUMBER ;
18  
19 /*------------------------------------------------------------------
20  * LEXER RULES
21  *------------------------------------------------------------------*/
22  
23 NUMBER  : (DIGIT)+ ;
24  
25 WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+  { $channel = HIDDEN; } ;
26  
27 fragment DIGIT  : '0'..'9' ;

这表明了通常模式:得到一个输入流,进行词法分析,得到标记流,进行语法分析,然后在语法分析中调用其中的方法。(每个语法规则在语法分析器中添加一个相应的方法)

 

 

posted @ 2012-11-28 23:59  郑蕊  阅读(2065)  评论(0编辑  收藏  举报