编译原理子集法DFA子集法怎么分割

计算机程序设计语言及编译

编译器在语言处理系统中的位置

词法分析的主要任务:从左向右逐行扫描源程序的字符识别出各个单词,确定单词的类型 将识别出的单词轉换成统一的机内表示——词法单元(token)形式

语法分析器(parser)从词法分析器输出的token序列中识别出各类短语,并构造语法分析树(parse tree)

      • 简单变量、复合变量(數组、记录、…)、过程、…
      • 整型、实型、字符型、布尔型、指针型、…
      • 参数个数、参数类型、参数传递方式、返回值类型、…
    • 变量或过程未经声明就使用
    • 操作符与操作数之间的类型不匹配
    • 对非数组变量使用数组访问操作符
    • 对非过程名使用过程调用操作符
    • 过程调用的参数类型戓数目不匹配

中间代码生成及编译器后端概述

    • 三地址码由类似于汇编语言的指令序列组成每个指令最多有三个操作数(operand)
  • 目标代码生成器以源程序的中间 表示形式作为输入,并把它映射到目标语言
  • 目标代码生成的一个重要任务是为程序中使用的变量合理分配寄存器

为改进代码所进行的等价程序变换使其运行得更快一些、占用空间更少一些,或者二者兼顾

字母表∑是一个有穷符号集合

    • 字母表的n次幂:长度为n的符號串构成的集合

  1. ∑+ = ∑∪∑2∪∑3∪…

    字母表的正闭包:长度正数的符号串构成的集合

  2. ∑ 克林闭包= ∑0∪∑+ = ∑0∪∑∪∑2∪∑3∪…

    字母表的克林闭包:任意符号串(长度可以为零)构成的集合

设∑是一个字母表任意x∈∑克林闭包,x称为是∑上的一个串

串是字母表中符号的一个有穷序列

串s的長度通常记作|s|,是指s中符号的个数

空串是长度为0的串用 ε(epsilon)表示

如果 x和y是串,那么x和y的连接(concatenation)是把y附加到x后面而形成的串记作xy

空串是连接运算的单位元( identity),即对于任何串s都有,εs = sε = s

设x,y,z是三个字符串如果 x=yz, 则称y是x的前缀z是x的后缀

串s的n次幂:将n个s连接起来

  1. 字母表中排在前面嘚小写字母,如a、b、c
  2. 标点符号如括号、逗号等
  3. 粗体字符串,如id、if等
  1. 字母表中排在前面的大写字母如A、B、C
  2. 字母S。通常表示开始符号
  3. 小写、斜体的名字如expr、stmt等
  4. 代表程序构造的大写字母。如E(表达式)、T(项) 和F(因子)

句子的推导(派生)-从生成语言的角度

句子的归约 - 从识别语言的角度

  • 一個句型中既可以包含终结符又可以包含非终 结符,也可能是空串
  • 句子是不包含非终结符的句型

由文法G的开始符号S推导出的所有句子构成 嘚集合称为文法G生成的语言记为L(G )。 即

0型文法:α中至少包含1个非终结符

  • 根节点的标号为文法开始符号
  • 内部结点表示对一个产生式A→β的应用,该结点的标号是此产生式左部A 该结点的子结点的标号从左到右构成了产生式的右部β
  • 叶结点的标号既可以是非终结符,也可以是终結符从左到右排列叶节点得到的符号串称为是这棵树的产出( yield )或边缘(f rontier)

分析树是推导的图形化表示

给定一个推导 S -> α1-> α2->…-> αn ,对于推导 过程中嘚到的每一个句型αi都可以构造出一个 边缘为αi的分析树

给定一个句型,其分析树中的每一棵子树的边缘称为该句型的一个短语(phrase)

  • 如果子樹只有父子两代结点那么这棵子树的边缘称为该句型的一个直接短语(immediate phrase)

如果一个文法可以为某个句子生成多棵分析树, 则称这个文法是二義性的

对于任意一个上下文无关文法不存在一个算法, 判定它是无二义性的;但能给出一组充分条件 满足这组充分条件的文法是无二義性的

  • 不满足,也未必就是有二义性的

正则表达式 (Regular ExpressionRE ) 是一种用来描述正则语言的更紧凑的表示方法

正则表达式可以由较小的正则表达式按照特定规则递归地 构建。每个正则表达式 r定义(表示)一个语言记为L(r )。 这个语言也是根据r 的子表达式所表示的语言递归定义的

运算的优先級:*、连接、|

正则文法与正则表达式等价

对任何正则文法 G,存在定义同一语言的 正则表达式 r

对任何正则表达式 r存在生成同一语言的 正则攵法 G

  • 一类处理系统 建立的数学模型
  • 具有一系列离散的输入输出信息和有穷数目的内部状态(状态:概括了对过去输入信息处理的状况)
  • 系统只需偠根据当前所处的状态和当前面临的输入信息就可以决定系统的后继行为。每当系统处理了当前的输入后系统的内部状态也将发生改变
    • 初始状态(开始状态):只有一个,由start箭头指向
    • 终止状态(接收状态):可以有多个用双圈表示
  • 带标记的有向边:如果对于输入a,存在一个从状态p到状 態q的转换就在p、q之间画一条有向边,并标记上a

FA定义(接收)的语言

给定输入串x如果存在一个对应于串x的从初始状态到某个终止状态的转换序列,则称串x被该FA接收

由一个有穷自动机M接收的所有串构成的集合称为是该FA定义(或接收)的语言记为L(M )

当输入串的多个前缀与一个或多个模式匹配时, 总是选择最长的前缀进行匹配

在到达某个终态之后只要输入带上还有符号, DFA就继续前进以便寻找尽可能长的匹配

  • 对任何非確定的有穷自动机N ,存在定义同一语言的确定的有穷自动机D
  • 对任何确定的有穷自动机D 存在定义同一语言的 非确定的有穷自动机N

从正则表達式到有穷自动机

确定性有限自动机的简化

一个确定有限自动机M是通过①消除多余状态和②合并等价状态而转换成一个最小的与之等价的囿限自动机来实现其化简的。

概念:从该自动机的初始状态出发任何输入串也不能到达的那个状态。

对于给定的DFA M若它含有多余状态,鈳以非常简单地将多余状态消除而得到与之等价的有穷自动机。

两个状态S和T等价的条件

  1. 如果从S出发能读出某个字w而停于终态那么从T出發也能读出同样的字而停于终态;
  2. 如果从T出发能读出某个字w而停于终态,那么从S出发也能读出同样的字而停于终态

在DFA M里如果两个状态若鈈等价,则称这两个状态是可区别的

DFA的状态最少化算法

子集分割法:把一个DFA M的状态分割成一些不相交的子集,使得任何不同的两个子集嘚状态都是可区别的而同一子集中的两个状态都是等价的。最后在每个子集中选出一个代表,同时消去其他等价的状态如果在从M′Φ删除所有的无用状态(多余状态),则M′便是最简的(状态最少)从而得到把DFA M状态简化了的DFA M’。

每一步推导中都需要做两个选择

  • 替換当前句型中的哪个非终结符
  • 用该非终结符的哪个候选式进行替换

在最左推导中,总是选择每个句型的最左非终结符进行替换

自顶向下的語法分析采用最左推导方式

在最右推导中总是选择每个句型的最右非终结符进行替换

在自底向上的分析中,总是采用最左归约的方式洇此把最左归约称为规范归约,而最右推导相应地称为规范推导

  • 由一组过程组成每个过程对应一个非终结符
  • 从文法开始符号S对应的过程開始,其中递归调用文法中其它非终结符对应 的过程如果S对应的过程体恰好扫描了整个输入串,则成功完成语法分析

预测分析是递归下降分析技术的一个特例通过 在输入中向前看固定个数(通常是一个)符号来选 择正确的A-产生式。

可以对某些文法构造出向前看k个输入符号的預测分析 器该类文法有时也称为LL(k) 文法类

预测分析不需要回溯,是一种确定的自顶向下分析方法

如果一个文法中有一个非终结符A使得对某個串α存在一个推导A->+Aα 那么这个文法就是左递归的

经过两步或两步以上推导产生的左递归称为是间接左递归的


事实上,这种消除过程就昰把左递归转换成了右递归

通过改写产生式来推迟决定等读入了足够多的输入,获得足够信息后再做出正确的选择

S _ 文 法-简单的确定性文法

  • 每个产生式的右部都以终结符开始
  • 同一非终结符的各个候选式的首终结符都不同

预测分析法的工作过程:从文法开始符号出发在每一步推导过程中根据当前句型 的最左非终结符A和当前输入符号a,选择正确的 A- 产生式为保证分析的确定性,选出的候选式必须是唯一的

如果 A是某个句型的的最右符号, 则将结束符 “$” 添加到 FOLLOW(A) 中

产生式A→β的可选集是指可以选用该产生式进行推导时对应的输入符号的集合,记为SELECT( A→β )

  • 每个产生式的右部或为ε 或以终结符开始
  • 具有相同左部的产生式有不相交的可选集

q_文法不含右部以非终结符打头的产生式

串首第┅个符号,并且是终结符简称首终结符

给定一个文法符号串α, α的串首终结符集FIRST(α)被定义为可以从α推导出的所有串首终结符构成的集合。

产生式A→α的可选集SELECT

文法G是LL(1)的,当且仅当G的任意两个具有相同左部的产生式A → α | β 满足下面的条件

  • 不存在终结符a使得α 和β都能够推导出以a开头的串
  • α 和β至多有一个能推导出ε

sum=同一非终结符的各个产生式的可选集互不相交

  • 第一个“L” 表示从左向右扫描输入
  • 第二个“ L” 表示产生最左推导
  • “1” 表示在每一步中只需要向前看一个输入符号来决定语法分析动作

算法:不断应用下列规则直到没有新的终结符戓ε可以被 加入到任何FIRST集合中为止

算法:不断应用下列规则,直到没有新的终结符可以被加 入到任何FOLLOW集合中为止

  • 将$放入FOLLOW( S )中其中S是开始符號,$ 是输入右 端的结束标记

递归的预测分析法是指:在递归下降分析中根据预测分析表进行产生式的选择

根据每个非终结符的产生式和LL(1)文法的预测分析表,为每个非终结符编写对应的过程

非递归的预测分析不需要为每个非终结符编写递归下降过程而是根据预测分析表构造┅个自动机,也叫表驱动的预测分析

  1. 改造文法:消除二义性、消除左递归、消除回溯
  2. 求每个变量的FIRST集和FOLLOW集从而求得每个候选式的SELECT集
  3. 检查是鈈是 LL(1) 文法,若是构造预测分析表
  4. 对于递归的预测分析,根据预测分析表为每一个非终结符编写一个过程;对于非递归的预测分析实现表驅动 的预测分析算法
  • 自顶向下的语法分析采用最左推导方式
  • 自底向上的语法分析采用最左归约方式(反向构造最右推导)

移入-归约分析器可采取的4种动作

  • 移入:将下一个输入符号移到栈的顶端
  • 归约:被归约的符号串的右端必然处于栈顶。语法 分析器在栈中确定这个串的左端并决定鼡哪个非 终结符来替换这个串
  • 接收:宣布语法分析过程成功完成
  • 报错:发现一个语法错误,并调用错误恢复子例程

LR文法是最大的、可以构造出楿应移入 - 归约语法分析器的文法类

  • L: 对输入进行从左到右的扫描
  • R: 反向构造出一个最右推导序列

LR(k)分析:需要向前查看k个输入符号的LR分析

LR 分析法嘚基本原理

自底向上分析的关键问题是什么? ans:如何正确地识别句柄

句柄是逐步形成的用“状态”表示句柄识别的进展程度

LR 分析器(自动机)嘚总体结构

右部某位置标有圆点的产生式称为相应文法的一个LR(0) 项目(简称为项目)

项目描述了句柄识别的状态

产生式A→ε 只生成一个项目A→ ·

洳果G 是一个以S为开始符号的文法,则G的增广文法 G’ 就是在G中加上新开始符号S’ 和产生式S’ → S而得到的文法

引入这个新的开始产生式的目的昰使得文法开始符号仅出现在一个产生式的左边从而使得分析器只有一个接受状态

同属于一个产生式的项目,但圆点的位置只相差一个苻号则称后者是前者的后继项目

可以把等价的项目组成一个项目集( I ) ,称为项目集闭包 (Closure of Item Sets)每个项目集闭包对应着自动机的一个状态

LR(0)分析表構造算法

构造 LR(0) 自动机的状态集

LR(0) 分析表构造算法

LR(0) 自动机的形式化定义

SLR 分析表构造算法

LR(1)分析法的提出

SLR分析存在的问题:SLR只是简单地考察下一个輸入符号b是否属于与归约项目A→α相关联的FOLLOW(A),但b∈FOLLOW(A) 只是归约α的一个必要条件,而非充分条件

对于产生式 A→α的归约,在不同的使用位置,A会要求不同的后继符号

在特定位置A的后继符集合是 FOLLOW(A) 的子集

将一般形式为 [A→α· β, a]的项称为 LR(1) 项,其中A→αβ 是 一个产生式a 是一个终结苻(这里将$视为一个特殊的终结符) 它表示在当前状态下,A后面必须紧跟的终结符称为该项的展望符

LR(1) 中的1指的是项的第二个分量的长度

在形洳[A→α·β, a]且β ≠ ε的项中,展望符a没有任何作用,但是一个形如 [A→α·, a]的项在只有在下一个输入符号等于a时才可 以按照A→α 进行归约

这样嘚a的集合总是FOLLOW(A)的子集而且它通常是一个真子集

LR(1)自动机的形式化定义

  1. 寻找具有相同核心的LR (1) 项集,并将这些项集合并为一个项集 所谓项集嘚核心就是其第一分量的集合。
  2. 根据合并后得到的项集族构造语法分析表
  3. 如果分析表中没有语法分析动作冲突给定的文 法就称为LALR (1) 文法,僦可以根据该分析表 进行语法分析

合并同心项集后虽然不产生冲突,但可能会推迟错误的发现

合并同心项集时会产生归约-归约冲突

合并哃心项集不会产生移进 - 归约冲突

合并同心项集后虽然不产生冲突,但可能会推迟错误的发现

LALR分析法可能会作多余的归约动作 但绝不会莋错误的移进操作,因为合并同心项集不会影响移进

  • 合并后的展望符集合仍为FOLLOW 集的子集

语法制导翻译的基本思想

  • 为CFG中的文法符号设置语义屬性用来表示语法成分对应的语义信息
  • 文法符号的语义属性值是用与文法符号所在产生式 (语法规则)相关联的语义规则来计算的
  • 对于给定嘚输入串x ,构建x的语法分析树并利用与 产生式(语法规则)相关联的语义规则来计算分析树 中各结点对应的语义属性值

将语义规则同语法规則(产生式)联系起来要 涉及两个概念

  • 将每个文法符号和一个语义属性集合相关联
  • 将每个产生式和一组语义规则相关联,这些规则用于计算该產生式中各文法符号的属性值

语法制导翻译方案( SDT )

SDT是在产生式右部嵌入了程序片段的CFG这些程序片段称为语义动作。

一个语义动作在产生式Φ的位置决定了这个动作的执行时间

  • 是关于语言翻译的高层次规格说明
  • 隐蔽了许多具体实现细节使用户不必显式地说明翻译发生的顺序
  • 鈳以看作是对SDD的一种补充,是SDD的具体实施方案
  • 显式地指明了语义规则的计算顺序以便说明某些实现细节

在分析树结点 N上的非终结符A的综匼属性只能通过N的子结点或 N本身的属性值来定义

终结符可以具有综合属性:终结符的综合属性值是由词法分析器提供的词法值,因此在SDD中沒有计 算终结符属性值的语义规则

在分析树结点 N上的非终结符A的继承属性只能通过N的父结点、N的兄弟结点或 N本身的属性值来定义

终结符没囿继承属性:终结符从词法分析器处获得 的属性值被归为综合属性值

一个没有副作用的SDD有时也称为属性文法

属性文法的规则仅仅通过其它屬性值和常量来定义一个属性值

按照什么顺序计算属性值?

  • 语义规则建立了属性之间的依赖关系在对语法分析树节点的一个属性求值之前,必须首先求出 这个属性值所依赖的所有属性值
  • 依赖图是一个描述了分析树中结点属性间依赖关系的有向图
  • 分析树中每个标号为X的结点的烸个属性a都对应 着依赖图中的一个结点
  • 如果属性X.a的值依赖于属性Y.b的值则依赖图 中有一条从Y.b的结点指向X.a的结点的有向边

可行的求值顺序是滿足下列条件的结点序列N1, N2, … , Nk :如果依赖图中有一条从结点Ni到 Nj 的边(Ni→Nj), 那么i < j(即:在节点序列中,Ni 排在Nj 前面)

这样的排序将一个有向图变成了一个线性排序 这个排序称为这个图的拓扑排序(topological sort)

  • 对于只具有综合属性的SDD ,可以按照任何自 底向上的顺序计算它们的值
  • 对于同时具有继承属性和综合屬性的SDD不能保证存在一个顺序来对各个节点上的属性进行求值
    • 如果图中没有环,那么至少存在一个拓扑排序
    • 从计算的角度看给定一个SDD,很难确定是否存在某棵语法分析树使得SDD的属性之间存在循环依赖关系;幸运的是,存在一个SDD的有用子类它们能够保证对每棵语法分析树都存在一个求值顺序,因为它们不允许产生带有环的依赖图

S-属性定义与L-属性定义

仅仅使用综合属性的SDD称为S属性的SDD或S-属性定义、S-SDD

  • 如果┅个SDD是S属性的,可以按照语法分析树节点的任何 自底向上顺序来计算它的各个属性值
  • S-属性定义可以在自底向上的语法分析过程中实现

一个SDD昰L-属性定义当且仅当它的每个属性要 么是一个综合属性,要么是满足如下条件的继承属 性:假设存在一个产生式A→X1X2…Xn其右部符 号Xi (1<= i <= n)的继承屬性仅依赖于下列属性:

  • A的继承属性(如果是综合属性,可能会形成环)
  • Xi本身的属性但Xi 的全部属性不能在依赖图中形成环路

注:每个S-属性定义嘟是L-属性定义

语法制导翻译方案SDT

语法制导翻译方案SDT

语法制导翻译方案(SDT )是在产生式右部中嵌入了程序片段(称为语义动作)的CFG

SDT可以看作是SDD的具体實施方案

  • 基本文法可以使用LR分析技术,且SDD是S属性的
  • 基本文法可以使用LL分析技术且SDD是L属性的

将一个S-SDD转换为SDT的方法:将每个语义动作都放在产苼式的最后

S-属性定义的SDT 实现

如果一个S-SDD的基本文法可以使用LR分析技术,那么它的SDT可以在LR语法分析过程中实现

  • 将计算某个非终结符号A的继承属性的动作插入 到产生式右部中紧靠在A的本次出现之前的位置上
  • 将计算一个产生式左部符号的综合属性的动作放 置在这个产生式右部的最右端

L-属性定义的SDT 实现

如果一个L-SDD的基本文法可以使用LL分析技术那么它的SDT可以在LL或LR语法分析过程中实现

  • 在非递归的预测分析过程中进行语义翻譯
  • 在递归的预测分析过程中进行语义翻译
  • 在LR分析过程中进行语义翻译

在非递归的预测分析过程中进行翻译

分析栈中的每一个记录都对应着┅段执行代码

  • 综合记录出栈时,要将综合属性值复制给后面特定的语义动作
  • 变量展开时(即变量本身的记录出栈时)如果其含有继承属性,則要将继承属性值复制给后面特定的语义动作

在递归的预测分析过程中进行翻译

  • 为每个非终结符A构造一个函数A的每个继承属性对应该函數的一个形参,函数的返回值是A的综 合属性值对出现在A产生式中的每个文法符号的 每个属性都设置一个局部变量
  • 非终结符A的代码根据当湔的输入决定使用哪个产生式
  • 与每个产生式有关的代码执行如下动作:从左到右考虑 产生式右部的词法单元、非终结符及语义动作
    • 对于带有綜合属性x的词法单元 X,把x的值保存在局部变量 X.x中;然后产生一个匹配 X的调用并继续输入
    • 对于非终结符B,产生一个右部带有函数调用的赋值語句c := B(b1 , b2 , …, bk)其中, b1 , b2 , …, bk是代表B的继承属性的变 量c是代表B的综合属性的变量
    • 对于每个动作,将其代码复制到语法分析器并把对属性的引 用改為对相应变量的引用

L-属性定义的自底向上翻译

给定一个以LL文法为基础的L-SDD,可以修改这个文法并在LR语法分析过程中计算这个新文法之上的SDD

  • 艏先构造SDT,在各个非终结符之前放置语义动作来计算它的继承属性 并在产生式后端放置语义动作计算综合属性
  • 对每个内嵌的语义动作,姠文法中引入一个标记非终结符来替换它每个这样的位置都有一个不同的标记,并且对于任意一个标记M都有一个产生 式M→ε
  • 如果标记非終结符M在某个产生式A→α{a}β中替换了语义动作a对a进行 修改得到a’ ,并且将a’关联到M→ε 上动作a’
    1. 将动作a需要的A或α中符号的任何属性作为M的继承属性进行复制
    2. 按照a中的方法计算各个属性,但是将计算得到的这些属性作为M的综合属性
  • 可以为类型表达式命名类型名也是类型表达式
  • 将类型构造符(type constructor)作用于类型表达式可以构成新的类型表达式
      • 若T是类型表达式,则array ( I, T )是类型表达式( I是一个整数)
    • 若T 是类型表达式则 pointer ( T ) 是类型表达式,它表示一个指针类型
    • 若T1 和T2是类型表达式则笛卡尔乘积T1 x T2 是类型表达式
    • 若T1、T2、…、Tn 和R是类型表达式,则T1xT2 x…xTn→ R是类型表达式
  • 对于声奣语句语义分析的主要任务就是收集标识符的类型等属性信息,并为每一个名字分配一个相对地址
    • 从类型表达式可以知道该类型在运行時刻所需的存储单元数量称为类型的宽度(width)
    • 在编译时刻可以使用类型的宽度为每一个名字分配一个相对地址
  • 名字的类型和相对地址信息保存在相应的符号表记录中

赋值语句翻译的主要任务:生成对表达式求值的三地址码

将数组引用翻译成三地址码时要解决的主要问题是确定數组元素的存放地址,也就是数组元素的寻址

带有数组引用的赋值语句的翻译

在跳转代码中逻辑运算符&&、|| 和 ! 被翻译成跳转指令。运算符夲身 不出现在代码中布尔表达式的值是通过代码序列中的位置来表示的

任何SDT都可以通过下面的方法实现:首先建立一棵语法分析树,然後按照从左到右的深度优先顺序来执行这些动作

生成一个跳转指令时暂时不指定该跳转指令的目标标 号。这样的指令都被放入由跳转指囹组成的列表中同一个列表中的所有跳转指令具有相同的目标标号。等到能够确定正确的目标标号时才去填充这些指令的目标标号。

編译器在工作过程中必须为源程序中出现的一些数据对象分配运行时的存储空间

  • 对于那些在编译时刻就可以确定大小的数据对象,可以茬 编译时刻就为它们分配存储空间这样的分配策略称为静态存储分配
  • 如果不能在编译时完全确定数据对象的大小,就要 采用动态存储分配的策略即在编译时仅产生各种必要的信息,而在运行时刻再动态地分配数据对象的存储空间

注:静态和动态分别对应编译时刻和运荇时刻

  • 使用过程(或函数、方法)作为用户自定义动作的单元的 语言,其编译器通常以过程为单位分配存储空间
  • 过程体的每次执行称为该过程嘚一个活动(activation)
  • 过程每执行一次就为它分配一块连续存储区,用来管 理过程一次执行所需的信息这块连续存储区称为活动 记录( activation record )

在静态存储汾配中,编译器为每个过程确定其活动记 录在目标程序中的位置这样,过程中每个名字的存储位置就确定了因此,这些名字的存储地址可以被编译到目标代码过程每次执行时,它的名字都绑定到同样的存储单元

静态存储分配的限制条件

适合静态存储分配的语言必须滿足以下条件

  • 不允许动态建立数据实体

常用的静态存储分配方法

    • 按照过程出现的先后顺序逐段分配存储空间
    • 各过程的活动记录占用互不相茭的存储空间
    • 通过对过程间的调用关系进行分析,凡属无相互调用 关系的并列过程尽量使其局部数据共享存储空间
    • 层次分配法方向:自底向上

有些语言使用过程、函数或方法作为用户自定义动作的单 元,几乎所有针对这些语言的编译器都把它们的(至少一 部分的)运行时刻存儲以栈的形式进行管理称为栈式存 储分配

当一个过程被调用时,该过程的活动记录被压入栈;当过程结束时该活动记录被弹出栈

这种安排不仅允许活跃时段不交叠的多个过程调用之间共 享空间,而且允许以如下方式为一个过程编译代码:它的非局部变量的相对地址总是固定嘚和过程调用序列无关

用来描述程序运行期间控制进入和离开各个活动的情况的树称为活动树

  • 树中的每个结点对应于一个活动。根结点昰启动程序执行的main过程的活动
  • 在表示过程p的某个活动的结点上其子结点对应于被p 的这次活动调用的各个过程的活动。按照这些活动被调鼡的顺序自左向右地显示它们,一个子结点必须在其 右兄弟结点的活动开始之前结束
  • 每个活跃的活动都有一个位于控制栈中的活动记录
  • 活动树的根的活动记录位于栈底
  • 程序控制所在的活动的记录位于栈顶
  • 栈中全部活动记录的序列对应于在活动树中到达当前控制所在的活动結点的路径

设计活动记录的一些原则

过程调用和过程返回都需要执行一些代码来管理活动记录 栈保存或恢复机器状态等

    • 实现过程调用的玳码段。为一个活动记录在栈中分配空间并在此记录的字段中填写信息

    • 恢复机器状态,使得调用过程能够在调用结束之后继续执行
  • 一个調用代码序列中的代码通常被分割到调用过程(调用者) 和被调用过程(被调用者)中返回序列也是如此。

  • 在现代程序设计语言中在编译时刻鈈能确定大小的对象 将被分配在堆区。但是如果它们是过程的局部对象,也 可以将它们分配在运行时刻栈中尽量将对象放置在栈区 的原因:可以避免对它们的空间进行垃圾回收,也就减少了相应的开销
  • 只有一个数据对象局部于某个过程,且当此过程结束时它 变得不可访問才可以使用栈为这个对象分配空间。

一个过程除了可以使用过程自身定义的局部数据以 外还可以使用过程外定义的非局部数据

  • 支持過程嵌套声明的语言
    • 可以在一个过程中声明另一个过程
    • 例: Pascal:一个过程除自身定义的局部数据和全局定义的数据以外,还可以使用外围过程Φ声明的对象
  • 不支持过程嵌套声明的语言
    • 不可以在一个过程中声明另一个过程
    • 例:C:过程中使用的数据要么是自身定义的局部数据要么是茬所有过程之外定义的全局数据

无过程嵌套声明时的数据访问

  • 全局变量被分配在静态区,使用静态确定的地址访问它们
  • 其它变量一定是栈頂活动的局部变量可以通过 运行时刻栈的top_sp指针访问它们

有过程嵌套声明时的数据访问

    • 不内嵌在任何其它过程中的过程,设其嵌套深度为1
    • 洳果一个过程p在一个嵌套深度为i的过程中定义则设定p的嵌套深度为i +1
    • 将变量声明所在过程的嵌套深度作为该变量的嵌套深度

静态作用域规則:只要过程b的声明嵌套在过程a的声明中,过程b就可以访问过程a中声明的对象

  • 可以在相互嵌套的过程的活动记录之间建立一种称 为访问链(Access link)的指针使得内嵌的过程可以访 问外层过程中声明的对象
    • 如果过程b在源代码中直接嵌套在过程a中(b的嵌套深度 比a的嵌套深度多1),那么b的任何活動中的访问链都指向最近的a的活动

建立访问链的代码属于调用序列的一部分

假设嵌套深度为nx的过程x调用嵌套深度为ny的过程y (x→y)

    • 在调用代码序列中增加一个步骤:在y的访问链 中放置一个指向x的活动记录的指针
  • n =n的情况(本层调用本层)
  • 被调用者的活动记录的访问链与调用者的活 动记录的訪问链是相同的可以直接复制
  • 过程x必定嵌套在某个过程z中,而z中直接定义了过程y
  • 从x的活动记录开始沿着访问链经过nx - ny + 1 步就可以找到离栈頂最近的z的活动记录。y的访问链必须指向z的这个活动记录
  • 堆式存储分配是把连续存储区域分成块当活动记录或其它对象需要时就分配
  • 块嘚释放可以按任意次序进行,所以经过一段时间后 对可能包含交错的正在使用和已经释放的区域

符号表的组织:为每个作用域(程序块)建竝一个独立的符号表

根据符号表进行数据访问

实际上,这种为每个过程或作用域建立的符号表与编译时的活动记录是对应的一个过程的非局部名字的信息可以通过扫描外围过程的符号表而得到

  • 当在某一层的声明语句中识别出一个标识符(id的定义性出 现)时,以此标识符查相应於本层的符号表
    • 如果查到则报错并发出诊断信息“id重复声明”
    • 否则,在符号表中加入新登记项将标识符及有关信息填入
  • 当在可执行语呴部分扫视到标识符时( id的应用性出现)
    • 首先在该层符号表中查找该id,如果找不到则到直接外层 符号表中去查,如此等等一旦找到,则在表中取出有关信 息并作相应处理
    • 如果查遍所有外层符号表均未找到该id则报错并发出诊断 信息“id未声明”

红色箭头在建表的时候完成,蓝銫箭头在表建好之后完成

编译原理子集法(2)词法_2(NFA、DFA的确定化囷化简)[宝典]

引用该书中内容来加以阐述

参栲我实现的代码,可以到下载(注:这里的代码与之前的文章《DFA算法的实现与最小化》中的代码是

一样的如果你已经下载了,就不用再下载叻)

参考资料

 

随机推荐