在(I)里,我已经简单介绍了ANTLR。现在,让我们继续ANTLR之旅,来真实感受一下它的强大。
和很多词法语法分析软件一样,我也以一个简易计算器作为example。
计算器需求
运算符:+,-,×,÷,(,)
支持整型和浮点型
测试样例
1 + 2 * 3 + 5 - 4/2
(1 + 2) * 3 + 5 - 4 / (2+2)
(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); } )? ;
(注:这里解释一下NUM这个规则。$setType是ANTLR内置函数,用来设置token类型。所以这个规则的意思就是当匹配到(’0′..’9′)*后,设置token类型为INT,当发现后面跟着小数点和数字后,重新设置token类型为REAL。相信细心的读者会有这种疑问,为什么不用两条规则,通过有无’.‘来匹配。关于这点,我们来回忆一下,ANTLR是采用LL(*),目前的release版本2.7.6只支持fixed-length lookahead,所以用两条规则会引起ambiguity。可以查看我在(I)里的回复,有更详细的描述。)
当然,我们还希望忽略空格,制表符,回车换行这种无意义字符的。
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
;
all done。一个计算器的语法程序就写好了。让我们来生成c++代码,实际测试一下.$java -cp /usr/share/java/antlr.jar antlr.Tool calc.gANTLR Parser Generator Version 2.7.6 (20060528) 1989-2005
$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
让我们来跑一下测试用例吧,:p
$ ./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
$
顺利通过,一切都是那么的自然,lol~。ok, 来些错误的。
$ ./Calc
(1+(1+3.5*(3-2))
line 1:18: expecting RPAREN, found ‘’
可以从错误中看到,还有一个没有匹配的’(‘。 有兴趣的话你就自己测试吧,:)
至此,我们的计算器的有语法解析部分基本已经完成,剩下的就是如何得到计算结果,这个留在后续文章吧。
完整的程序可以从这里下载:calc.tar.gz
enjoy yourself! Any advice prefer.