乔姆斯基把文法法分成四种类型,即0型、1型、2型和3型。这几种文法类型的概念一定要掌握,是一个非常重要的考点。对于这几种文法,一般书上都只有简单的概念介绍,比较抽象,所以很多学员都没有真正理解。下面我将把概念结合例题进行讲解。
0型文法
设G=(VN,VT,P,S),如果它的每个产生式α→β是这样一种结构:α∈(VN∪VT)*且至少含有一个非终结符,而 β∈(VN∪VT)*,则G是一个0型文法。0型文法也称短语文法。一个非常重要的理论结果是:0型文法的能力相当于图灵机(Turing)。或者说,任 何0型文语言都是递归可枚举的,反之,递归可枚举集必定是一个0型语言。0型文法是这几类文法中,限制最少的一个,所以我们在试题中见到的,至少是0型文 法。
1型文法
1型文法也叫上下文有关文法,此文法对应于线性有界自动机。它是在0型文法的基础上每一个α→β,都有|β|>=|α|。这里的|β|表示的是β的长度。
注意:虽然要求|β|>=|α|,但有一特例:α→ε也满足1型文法。
如有A->Ba则|β|=2,|α|=1符合1型文法要求。反之,如aA->a,则不符合1型文法。
2型文法
2型文法也叫上下文无关文法,它对应于下推自动机。2型文法是在1型文法的基础上,再满足:每一个α→β都有α是非终结符。如A->Ba,符合2型文法要求。
如Ab->Bab虽然符合1型文法要求,但不符合2型文法要求,因为其α=Ab,而Ab不是一个非终结符。
3型文法
3型文法也叫正规文法,它对应于有限状态自动机。它是在2型文法的基础上满足:A→α|αB(右线性)或A→α|Bα(左线性)。
如有:A->a,A->aB,B->a,B->cB,则符合3型文法的要求。但如果推导 为:A->ab,A->aB,B->a,B->cB或推导 为:A->a,A->Ba,B->a,B->cB则不符合3型方法的要求了。具体的说,例子 A->ab,A->aB,B->a,B->cB中的A->ab不符合3型文法的定义,如果把后面的ab,改成“一个非终结 符+一个终结符”的形式(即为aB)就对了。例子A->a,A->Ba,B->a,B->cB中如果把B->cB改为 B->Bc的形式就对了,因为A→α|αB(右线性)和A→α|Bα(左线性)两套规则不能同时出现在一个语法中,只能完全满足其中的一个,才能算 3型文法。
注意:上面例子中的大写字母表示的是非终结符,而小写字母表示的是终结符。
如果文法G中的某个句子存在不只一棵语法树,则称该句子是二义性的。如果文法含有二义性的句子,则称该文法是二义性的。
1. DFA对于文本串里的每一个字符只需扫描一次,比较快,但特性较少;NFA要翻来覆去吃字符、吐字符,速度慢,但是特性丰富,所以反而应用广泛,当今主要的正则表达式引擎,如Perl、Ruby、Python的re模块、Java和.NET的regex库,都是NFA的。
2. 只有NFA才支持lazy和backreference等特性;
3. NFA急于邀功请赏,所以最左子正则式优先匹配成功,因此偶尔会错过最佳匹配结果;DFA则是“最长的左子正则式优先匹配成功”。
4. NFA缺省采用greedy量词(见item 4);
5. NFA可能会陷入递归调用的陷阱而表现得性能极差。
两种引擎的工作方式完全不同,一个(NFA)以表达式为主导,一个(DFA)以文本为主导!一般而论,DFA引擎则搜索更快一些!但是NFA以表达式为主导,反而更容易操纵,因此一般程序员更偏爱NFA引擎!
一个文法,如果他的任何一个产生式的右部都不包含连个连续的非终结符,那么则称之为算符文法,比如说加减乘除都是算符文法。
算符优先文法是一种自下而上的分析方法,其文法的特点是文法的产生式中不含两个相邻的非终结符。自上而下的分析方法,通常要求文法的产生式不含左递归,如LL(I)文法就是一种可以自上而下分析的文法。
定义:
假定G是不含ε- 产生式的算符文法。对于任何一对终结符a、b,我们说:
(1)a等于b 当且仅当文法G中含有形如P→ ···ab···或P→···aQb···的产生式;
(2)a小于b 当且仅当G中含有形如P→···aR···的产生式,而R(+=>)b···或R(+=>)Qb···;
(3)a大于b 当且仅当G中含有形如P→···Rb···的产生式,而R(+=>)···a或R(+=>)···aQ;
如果一个算符文法G中的任何终结符对(a,b)最多满足下述三个条件之一:
a=b,ab
则称G是一个算符优先文法。
一个形式文法G是下述元素构成的一个四元组(N, Σ, P, S):
(Σ ∪ N)*中的字符串→ (Σ ∪ N)* 中的字符串(这里的*是克莱尼星号),并且产生式左侧的字符串中必须至少包括一个非终结符号。
一个由形式文法G = (N, Σ, P, S)产生的语言是所有如下形式的字符串集合,这些字符串全部由“终结符号”集Σ中符号构成,并且可以从“初始符号”S出发,不断应用P中的“产生式规则”而得到。
如果文法G中的某个句子存在不只一棵语法树,则称该句子是二义性的(文法如果有两个不同的最右(最左)推导)。如果文法含有二义性的句子,则称该文法是二义性的。提出一些限制条件,称为无二义性的充分条件,当文法满足这些条件时,就可以判定文法是无二义性的. 确定一种编译算法,使该算法满足无二义性充分条件.”
class Timer {
public :
Timer ();
};
class TimeKeeper {
public :
TimeKeeper (const Timer & t );
int get_time ();
};
int main () {
TimeKeeper time_keeper (Timer ());
return time_keeper 。get_time ();
}
TimeKeeper time_keeper (Timer ());
看起来很模糊,因为它可以被解释为
大多数程序员都期望第一个,但C ++标准要求将其解释为第二个。
有些语言使用过程、函数或方法作为用户自定义动作的单元,几乎所有针对这些语言的编译器都把它们的(至少一部分的)运行时刻存储以栈的形式进行管理,称为栈式存储分配
3.1 活动树
用来描述程序运行期间控制进入和离开各个活动的情况的树称为活动树
控制栈
3.2 设计活动记录的一些原则
在调用者和被调用者之间传递的值一般被放在被调用者的活动记录的开始位置,这样它们可以尽可能地靠近调用者的活动记录
固定长度的项被放置在中间位置:控制连、访问链、机器状态字
在早期不知道大小的项被放置在活动记录的尾部
栈顶指针寄存器top_sp指向活动记录中局部数据开始的位置,以该位置作为基地址
删除多余运算、代码外提、强度削弱、变换控制条件、合并已知量和复写传播、删除无用赋值。局部优化、循环优化、全局优化。
第一种:把属性种类完全相同的那些符号组织在一起,构造出表项是分别为等长的多个符号表。这样组织的最大优点是每个符号表的属性个数和结构完全相同。
第二种:把所有语言中的符号都组织在一张符号表中。组成一张包括了所有属性的庞大的符号表。这样组织方式的最大优点是总体管理非常集中单一,且不同种类符号的共同属性可一致地管理和处理。
第三种:折衷方式是根据符号属性相似程度分类组织成若干张表,每张表中记录的符号都有比较多的相同属性。这种折衷的组织方式在管理复杂性及时空效率方面都取得折衷的效果,并且对复杂性和效率的取舍可由设计者根据自己的经验和要求及目标系统的客观环境和需求进行选择和调整。
G[S]:
S → N0 | N5
N → ND |ε
D → 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
设有如下的基本块:
X:=B*C
Y:=B/C
Z:=X+Y
W:=9*Z
G:=B*C
T:=G*G
W:=T*G
L:=W
M:=L
画出该基本块的DAG图。假设只有L在基本块后面还要被引用,给出优化后的四元式序列。