First集和Follow集
LL(1)语法的功能
回退会导致分析器的效率问题。
如果我们能够预测每个步骤中每个产品的正确选择,这将是避免的。
预测分析处理:
- 从开始符号开始,根据最左边的非终端符号A和输入符号“a”,选择正确的产生式。
- 所选产品必须是唯一的,以确保分析的正确性。
- 如何选择正确的产生式?
S-语法(Korenjak&Hopcroft,1966)
要使预测处理正常工作,必须遵循以下步骤:
- 每个产生式右测必须以终结符开始。
- 对于相同的非终结符,每个候选产生式的第一个终结符必须不同。
所以,我们可以根据输入符号选择一个产生式(不会产生冲突)。
ε产生式(右部为空符号串的产生式)是不包含在内的。
语法的应用受到限制。
First集合和FOLLOW集合的作用
First集合的定义:该规则派生出的字符串的第一个终结符集合。
Follow集合的定义:该规则派生的字符串后面可以立即出现的终结符集合。
规则的一个例子
就编程语言的编译器而言,我们有一个用于IF语句的规则:
- if_statement => IF Boolean_expression THEN statement;
- 我们期望FIRST集至少包含IF token。
- 如果IF语句后面可以跟任何编程语句,那么FOLLOW集将由所有可以启动任何编程语句的tokens组成。例如,IF,WHILE等。
First集合
我们首先为任何由终端和非终端组成的字符串a定义集合FIRST(a)
这是派生自a的字符串的第一个(即最左边)终结符的所有终结符的集合。
也称为 Left Terminal Set
Follow集合
例子:
Z -> XY
Y -> start_y Q
语法告诉我们X后面紧跟着Y。
Y以token start_y开始,所以start_y是X的追随者。
First集合举例
meal → first_course second_course dessert;
这个产生式法则告诉我们,一顿饭由第一道菜、第二道菜和甜点组成。
first_course → SOUP | SALAD | ε;
ε是一个表示什么都没有的特殊符号
换句话说,用餐者可能会选择跳过第一道菜
这意味着我们的语法包含一个空结果
second_course →CHICKEN | FISH | BEEF | LAMB
所以一顿饭可以是汤和鸡肉,也可以只是鸡肉
FIRST(first_course) = {SOUP , SALAD, ε };
FIRST(second_course) = {CHICKEN, FISH, BEEF,LAMB};
因为first_course生成规则可以生成空的,这意味着我们在处理meal时可能看到的第一个终结符实际上属于first_course后面的规则,即second_course。
所以我们必须将second_course的集合(并集)添加到first_course的集合中。
FIRST(meal)=FIRST(first_course) U FIRST(second_course)={SOUP , SALAD} U {CHICKEN,FISH,BEEF,LAMB}={SOUP , SALAD, CHICKEN,FISH,BEEF,LAMB};
Follow集合举例
meal →first_course second_course dessert;
first_course →SOUP|SALAD| ε;
second_course →CHICKEN|FISH|BEEF|LAMB;
FIRST(second_course) = {CHICKEN,FISH,BEEF,LAMB};
first_course后面是什么? The FIRST(second_course)!
So, FOLLOW(first_course)= {chicken, fish, beef, lamb};
FIRST集和FOLLOW集的构造
如果X可以生成空字符串,FIRST(XY)不仅仅是FIRST(X),它是FIRST(X) U FIRST(Y);
这是因为FIRST(X)可能是{ε}(空集合),因此我们必须考虑什么是Y的FIRST集合;
为了构造FIRST(α),其中α→X1 X2…Xn (假设简单的BNF格式):
如果X1是终结符,则将其加入FIRST(α)
如果X1是非终结符,语法规则为X1→β,则将FIRST(X1)加入FIRST(α)
如果X1可以生成空字符串,如果X1可以生成空字符串,那么考虑可以开始X2的终结符,依此类推
FOLLOW集的构造I
night_out →meal drink;
drink →BEER|WINE|VODKA;
FOLLOW(meal) = {BEER, WINE, VODKA};
After we’ve had a meal, we go for a drink
What can follow a meal?
寻找meal在右手边的产生式
看看后面是什么
FOLLOW集合的构造II
为某些非终结符X构造FOLLOW(X):
把EOF放在FOLLOW(distinguished symbol)中。
如果有一个产生式 Y -> α X β, 将FIRST(β)添加到FOLLOW(X)
如果有一个产生式 Y -> α X, 或Y -> α X β 并且 β⇒ ε, 将FOLLOW(Y)添加到FOLLOW(X)。
区分符号是语法的“根”,即对于源文本,它可能是程序。
First算法的一个更简单的表述
语法中没有空
First(A)其中A->X1X2…XN
- 如果X1是终结符,把它加到FIRST(A)中。
- 如果X1是具有语法规则的非终结符,则将FIRST(X1)添加到FIRST(a)。
所以我们依次考虑每个非终结符。
Follow算法的一个更简单表述
Follow集
- 初始化FOLLOW set为{}
- 把EOF放在FOLLOW(distinguished symbol)中
- 如果Y→AXB,将FIRST(B)加入FOLLOW(X)中
- 如果Y→ZXc,将“c"加入FOLLOW(X)中
- 如果Y→AXB,将FOLLOW(B)加入FOLLOW(Y)中
- 继续执行,直到对下面的集合没有进一步的更改
再谈Follow算法
为了生成下面的集合,我们必须查看生成规则的右侧。
我们将其视为一个有序的项列表,其中项要么是终结符,要么是非终结符。
对于每一个非终结符的出现,我们都要看它后面的内容。
如果它是一个终结符,我们将它添加到下面的集合中。
如果它是一个非终结符,我们将该非终结符的FIRST集合添加到接下来的集合中。
如果它是一个非终结符,则将LHS(右手边)的FOLLOW集添加到该非终结符的FOLLOW集中。
Thanks to Dr. John: Some contents are from their slides.