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)
如上面的例子:
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)
相关文章推荐
- How To Build a Yacc?(13)
- How To Build a Yacc?(14)
- How To Build a Yacc?(1)
- How To Build a Yacc Series
- How To Build a Yacc?(3)
- How To Build a Yacc?(9)
- How To Build a Yacc
- How To Build a Yacc?(4)
- How To Build a Yacc?(10)
- How To Build a Yacc?(5)
- How To Build a Yacc?(6)
- How To Build a Yacc?(11)
- How To Build a Yacc?(7)
- How To Build a Yacc?(8)
- How To Build a Yacc?(12)
- How to build Android adb for ARM
- How to Build CyanogenMod for One X (codename: endeavoru)
- How_to_build_an_ARM/DSP_Hello_World_program_on_the_DaVinci_EVM
- How to Build FFmpeg for Android
- XPages Example: Building a Custom Control - How to build a Custom Multi-FileUpload Control that you can drop on any XPag