您的位置:首页 > 产品设计 > UI/UE

How To Build a Yacc?(2)

2005-12-19 17:36 441 查看
如何识别一段代码是否符合定义的文法?

如上面的例子:
function foo(kick, so, by);

首先,技术上来说,代码文本是一段字符流,f, u, n, c....,而我们文法识别的最小级别是符号(token), 所以需要将其转化为符号流,这个功能可以很容易的用lex实现,这个步骤不是讲述重点,不加详细叙述。

最直接的识别方法,以function_decl文法为例,我们从符号流中取一个当前符号,然后判断这个符号是否属于开始符号(function_decl)的某个产生式的第一个符号, 如果找到了这样一个产生式,那么认为应该按照这个产生式进行展开,匹配并丢弃当前这个符号,并期望符号流中余下的符号能匹配该产生式剩余的符号;那么继续从符号流中取去下一个符号,继续上面的步骤。

如果要用一个算法来描述它,那么看起来,象这个样子。
// 匹配一个符号token...
void match(token)
{
if (current_token == token) current_token = get_next_token();
else error("expect {token}, but give {current_token}")
}

// function_decl 用来匹配一个函数声明语句;
// function_decl 的产生式为:
// function_decl := function func_name ( argment_list );
void function_decl( )
{
current_token = get_next_token(); // 取出一个符号
match(function); // 匹配function
func_name(); // 如果已经匹配,那么接下来应该匹配函数名字了
match('('); // 匹配'('
argument_list(); // 接下来应该参数列表
match(')'); // 匹配')'
}

void func_name()
{
match(id);
}

void argument_list()
{
while (current_token == id) {
match(",");
}
}

如此简单?是不是?
以上的分析技术被称为递归下降分析技术,它对大多数简单的语法规则非常有效。
这种分析方法可以很容易的被归纳成一些简单的规则,根据这些规则,我们可以方便的编制分析程序。
在阐述这些规则之前,有必要介绍一个概念:fisrt集合。

什么是fisrt集合?
一个产生式的first项目就是这个产生式(production)的匹配第一个非终结符号。一套文法的所有产生式的first项目组成了first集合。求解first集合的方法:对于production: S = ABC
first(ABC) , 如果A是一个terminal, 那么first(ABC)= A, 如果A是一个NONTERMINAL, 那么first(ABC) = first(A), 如果A最终被推出一个空的符号,那么first(ABC) = first(BC), 依次类推。
这个概念之所以重要,是因为在递归下降算法中,在匹配一个非终结符的过程中,需要检测当前符号流中的符号是否属于该非终结符的所有产生式的first集合;如果属于,则用该产生式来扩展这个非终结符。

如何编写递归下降解析程序?
是时候总结一下规律了,对于每个产生式a来说,我们定义T(a) 是匹配a的程序代码:

when: a = A (A是terminal)
T(a):
if (t == A) t = get_next_token();
else error (t 是当前符号,get_next_token取得下一个符号)

when: a = X (X是nonterminal)
T(a): X(); 定义一个X的函数,实现由X的产生式定义。

when: a = a1 | a2 | a3 | ... | aN
T(a):
if (t <- First(a1) ) T(a1)
else if (t <- First(a2)) T(a2)
...
else if (t <- First(aN)) T(aN)
else error

when: a = a1 a2 ... aN
T(a): T(a1) T(a2) ... T(aN)

when: a = {a1}
T(a): while (t <- First(a1)) T(a1)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: