上一篇博文介绍了对输入字符流的预处理,将字符流转化为Token流。经过这一步转化后,就可以使用上下文无关文法进行分析了。本文介绍对Token流的分析过程。
我们使用递归下降法来实现语法的解析。递归下降法的特点是,为每一个非终结符定义一个函数,在这个函数里需要定义所有以该非终结符为左部的产生式。
我们定义了下面的非终结符。
- <prog>表示整个源代码段,它是文法定义中的初始非终结符。
- <line_comment>表示代码段中的行注释片断。
- <block_comment>表示代码段中的块注释片断。
- <quote>表示代码段中的引用字符串,即C语言中的字符串常量。
- <norm_block>表示代码段中非注释,非引用的片段
下面将对这些非终结符逐一分析。
1. prog
void prog(BufferedReader & reader)
{
while(true)
{
Token t = reader.readToken();
if(t.type == Token::END_OF_FILE)break;
reader.pushToken(t);
if(t.type == Token::BLCOK_COMMENT_START)blockComment(reader);
else if(t.type == Token::LINE_COMMENT_START)lineComment(reader);
else if(t.type == Token::DOUBLE_QUOTE_MARK)quote(reader);
else normBlock(reader);
}
}
- 如果第一个token为/*,则属于blockComment(代码第9行)
- 如果第一个token为//,则属于lineComment(代码第10行)
- 如果第一个token为双引号",则属于quote(代码第11行)
- 否则,属于normBlock(代码第12行)
这种只需要看一个终结符就能够判断用哪个非终结符或者产生式归约的文法特性称为1向前看特性。需要注意的是一般的上下文无关文法不具备这种特性。所以在解析一般的上下文无关文法时可能需要回溯。而为了实现高效编译,一般的程序设计语言都满足1向前看的特性。
2. quote
void quote(BufferedReader & reader)
{
reader.readToken().print();
while(true)
{
Token t = reader.readToken();
if(t.type == Token::END_OF_FILE)break;
t.print();
if(t.type == Token::DOUBLE_QUOTE_MARK)break;
}
}
代码第3行首先将第1个双引号读入并输出。之后的循环依次读入字符,直到出现引号结束。
注意:
- 如果在引用字符串中出现引号”,需要写成转义字符\",对应token的type为CONVERT_CHAR。
- 在引用中可能会出现行注释和块注释的开始记号,我们将它看做是能够在引用中出现的合法token,并没有什么妨碍。
3. lineComment
void lineComment(BufferedReader & reader)
{
reader.in_comment = true;
while(true)
{
Token t = reader.readToken();
if(t.type == Token::END_OF_FILE || t.type == Token::ENTER)break;
}
reader.in_comment = false;
}
在处理注释要注意下面几点。
- 设置reader对象的in_comment标记,告诉reader现在正在处理注释,这样reader对象就会把'\'当做是普通的字符。
- 处理注释时,不要输出token,这样就达到了删除注释的目的。
4. blockComment与lineComment解析相似,故略。
5. normBlock
void normBlock(BufferedReader & reader)
{
while(true)
{
Token t = reader.readToken();
if(t.type == Token::END_OF_FILE)break;
if(t.type != Token::NORM_CHAR && t.type != Token::ENTER && t.type != Token::CONVERT_CHAR)
{
reader.pushToken(t);
break;
}
t.print();
}
}
normBlock与blockComment,lineComment,quote不同。blockComment,lineComment和quote有明显的起始和结束token,可是normBlock没有。因此在解析normBlock时,总是看当前token是否是normBlock定义的合法token。如果合法,则将该token接收进来。否则,normBlcok的解析结束。这种解析策略涉及到上下文无关文法的空串产生式,下一篇博文将详细讨论。