首先,遵循两个语法中的非终端

时间:2010-02-17 06:46:18

标签: grammar context-free-grammar

给出以下语法

S -> L=L  
s -> L  
L -> *L  
L -> id  

非终端的第一个和后续是什么?

如果语法改为

S -> L=R  
S -> R  
L -> *R  
L -> id  
R -> L  

首先是什么?

1 个答案:

答案 0 :(得分:23)

当我在大学学习编译课程时,我根本不了解FIRST和FOLLOWS。我实现了龙书中描述的算法,但我不知道发生了什么。我想我现在就做。

我假设你有一本书给出了这两套的正式定义,这本书完全不可理解。我会尝试对它们进行非正式的描述,希望这有助于你理解你书中的内容。

第一组是您可能看到的非终端扩展的第一部分的终端集。 FOLLOWS集是您在非终端扩展后可能看到的一组终端。

在您的第一个语法中,只有三种终端:=*id。 (您也可以将$(输入结束符号)视为终端。)唯一的非终端是S(语句)和L(左值 - - 你可以指定的“东西”。

将FIRST(S)视为可能启动语句的非终端集合。直觉上,您知道您没有使用=开始声明。所以你不希望它出现在FIRST(S)中。

那么声明如何开始?有两个生产规则可以定义S的外观,它们都以L开头。因此,要弄清楚FIRST(S)中的内容,你真的必须看看FIRST(L)中的内容。有两个生产规则可以定义Lvalue的外观:它以*id开头。所以FIRST(S)= FIRST(L)= {*id}。

以下(S)很容易。 S后面没有任何内容,因为它是起始符号。因此,FOLLOWS(S)中唯一的东西是$,即输入结束符号。

关注(L)有点棘手。您必须查看出现L的每个生产规则,并查看其后的内容。在第一条规则中,您会看到=可能会跟随L。所以=在FOLLOWS(L)中。但是您还注意到该规则在生产规则的末尾还有另一个L。因此,L之后的另一件事就是可以跟随生产的任何事情。我们已经发现,S生成之后唯一可以遵循的是输入结束。所以关注(L)= {=$}。 (如果您查看其他制作规则,L始终会显示在其末尾,因此您只需从这些规则中获取$。)

看看这个Easy Explanation,现在忽略关于ϵ的所有内容,因为你没有任何包含空字符串的产品。在“First Sets规则”下,规则#1,#3和#4.1应该是有意义的。在“遵循规则集”下,规则#1,#2和#3应该是有意义的。

当您的生产规则中有ϵ时,事情变得更加复杂。假设你有类似的东西:

D -> S C T id = V  // Declaration is [Static] [Const] Type id = Value
S -> static | ϵ    // The 'static' keyword is optional
C -> const | ϵ     // The 'const' keyword is optional
T -> int | float   // The Type is mandatory and is either 'int' or 'float'
V -> ...           // The Value gets complicated, not important here.

现在,如果你想计算FIRST(D),你不能只看FIRST(S),因为S可能是“空的”。您直观地知道FIRST(D)是{staticconstintfloat}。这种直觉在规则#4.2中被编纂。在“简单说明”规则中,将此示例中的SCT视为Y1Y2Y3

如果你想计算FOLLOWS(S),你不能只看FIRST(C),因为它可能是空的,所以你还要看FIRST(T)。所以关注(S)= {constintfloat}。你可以通过应用“跟随集规则”#2和#4(或多或少)来实现这一点。

我希望有帮助,你可以自己找出第二个语法的第一个和后续个。

如果有帮助,R表示Rvalue - 您无法分配的“事物”,例如常量或文字。 Lvalue也可以作为Rvalue(但不是相反)。

a = 2;  // a is an lvalue, 2 is an rvalue
a = b;  // a is an lvalue, b is an lvalue, but in this context it's an rvalue
2 = a;  // invalid because 2 cannot be an lvalue
2 = 3;  // invalid, same reason.
*4 = b; // Valid!  You would almost never write code like this, but it is
        // grammatically correct: dereferencing an Rvalue gives you an Lvalue.