编译原理知识点

编译原理知识点【第一章 引言】

编译原理的知识点是在https://www.jianshu.com/p/eb63d31ad638基础上进行适当的补充,以下图片的截图主要来自这里https://www.bilibili.com/video/BV12741147J3?t=832。如果整理有误,请好心人帮忙指出。

文章目录

    • 编译原理知识点【第一章 引言】
    • 第一章 引言
      • 1.从面向机器的语言到面向人类的语言
      • 2.语言之间的翻译
      • 3. 编译器与解释器
        • 1)语言翻译的两种基本形态
        • 2)各自特点
      • 4. 编译器的工作原理与基本组成
        • 0)通用程序设计语言的主要成份
        • 1)以过程为基本结构的程序设计语言的组成
        • 2)以阶段划分编译器
        • 3)编译器各阶段工作
        • 4)编译器的分析\综合模式
      • 5. 编译器扫描的遍数
    • 第二章 词法分析
      • 1. 词法分析中的若干问题
        • 1) 记号、模式与单词
        • 2) 词法分析器的作用与工作方式
      • 2. 模式的形式化描述
        • 1) 字符串与语言
        • 2) 正规式与正规集
        • 3) 简化正规式描述
      • 3. 记号的识别——有限自动机
        • 1) 不确定的有限自动机
        • 2) 确定的有限自动机
        • 3)DFA和NFA的区别
      • 4. 从正规式到词法分析器
        • 1) 从正规式到NFA
        • 2) 从NFA到DFA
        • 3) 最小化DFA
      • 5. 从DFA构造词法分析器
    • 第三章 语法分析
      • 1. 语法分析的若干问题
        • 1)语法分析器的作用
        • 2)语法错误的处理原则
      • 2. 上下文无关文法(CFG)
        • 1)上下文无关文法
        • 2)CFG产生语言的基本方法-推导
        • 3)推导、分析树与语法树
        • 4)二义性与二义性的消除
      • 3. 语法与文法简介
        • 1)正规式与上下文无关文法
        • 2)上下文有关文法CSG
        • 3)文法类型
      • 4. 自上而下语法分析
        • 1)消除左递归,变成右递归文法
          • 1.1)消除直接左递归
          • 1.2)消除左递归
          • 1.3)FIRST集合
          • 1.4)如何计算FIRST(α)?
        • 2)提取左因子
          • 2.1)提取文法的左因子
          • 2.2)FOLLOW集合
          • 2.3)如何计算FOLLOW(A)
          • 2.4)LL(1)文法
          • 2.5)LL(1)分析法
        • 3)递归下降分析
        • 4)预测分析程序(器)
          • 4.1)预测分析程序的构成:
          • 4.2)预测分析过程
          • 4.3)预测分析示例
          • 4.4)如何填写分析表
      • 5. 自下而上语法分析
        • 1)自下而上分析的基本思想
          • 1.1)基本思想和基本概念(短语,直接短语,句柄)
          • 1.2)移进-规约
        • 2)算符优先分析法
          • 2.1)什么是算符优先文法
          • 2.2)FIRSTVT集合和LASTVT集合
          • 2.3)FIRSTVT的计算
          • 2.4)LASTVT的计算
          • 2.5)构造优先关系表
        • 3)LR分析法
          • 3.1)LR分析法框架
          • 3.2)LR分析表概述
          • 3.3)LR分析过程
          • 3.4)前缀 & 活前缀
          • 3.5)LR(0)分析表如何构造(拓广文法,LR(0)项目,NFA,DFA)
          • 3.6)移进规约冲突& 规约规约冲突 & SLR(1)冲突解决方法
          • 3.7)SLR(1)分析表的构造
          • 3.8)SLR(1)存在的问题
          • 3.9)LR(1)分析表的构造
          • 3.10)LR(1)分析表的分析过程
          • 3.11)LR(0), SLR(1), LALR(1), LR(1)对比与分析
    • 第四章 静态语义分析(待整理)
      • 1. 语法制导翻译简介
      • 2. 中间代码简介
      • 3. 符号表简介
      • 4. 声明语句的翻译
      • 5. 简单算术表达式与赋值句
      • 6. 数组元素的引用
      • 7. 布尔表达式
      • 8. 控制语句

第一章 引言

1.从面向机器的语言到面向人类的语言

汇编指令: 用符号表示的指令被称为汇编指令
汇编语言: 汇编指令的集合称为汇编语言

2.语言之间的翻译

转换(也被称为预处理):高级语言之间的翻译,如FORTRANADA的转换
编译: 高级语言可以直接翻译成机器语言,也可以翻译成汇编语言,这两个翻译过程称为编译
汇编:汇编语言到机器语言的翻译被称为汇编
交叉汇编: 将一个汇编语言程序汇编成为可在另一机器上运行的机器指令成为交叉汇编
反汇编:机器语言翻译成汇编语言
反编译:汇编语言翻译成高级语言

编译原理知识点_第1张图片

3. 编译器与解释器

1)语言翻译的两种基本形态

编译原理知识点_第2张图片

解释器与编译器的主要区别:运行目标程序时的控制权在解释器不在目标程序.

2)各自特点

  • 编译器:

    • 工作效率高,即时间快、空间省
    • 交互性与动态性差 ,可移植性差
  • 解释器:

    • 工作效率低,即时间慢,空间费;
    • 交互性与动态性好,可移植性好。
  • 共同点:

    ​均完成对源程序的翻译

  • 差异:

    ​编译器采用先翻译后执行,解释器采用边翻译边执行.

4. 编译器的工作原理与基本组成

0)通用程序设计语言的主要成份

​ 声明+操作=完整定义

1)以过程为基本结构的程序设计语言的组成

  • 声明性语句:提供操作对象的性质,如数据类型、值、作用域等;
  • 操作性语句:确定操作的计算次序,完成实际操作。
  • 过程定义 = 过程头+过程体

2)以阶段划分编译器

编译原理知识点_第3张图片

注:符号表管理器出错处理贯穿编译器工作的各个阶段.

3)编译器各阶段工作

https://www.cnblogs.com/anthony007/p/4241189.html

1> 词法分析:

  • 词法分析的输入是源程序输出是识别出的记号流
  • 目的是识别单词. 至少分以下几类:关键字(保留字)、标识符、字面量、特殊符号
  • 词法分析的第一阶段即扫描器,通常基于有限状态自动机。有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。有限状态自动机可以表示为一个有向图。有限状态自动机是自动机理论的研究对象。
  • 扫描器能够识别其所能处理的标记中可能包含的所有字符序列(单个这样的字符序列即前面所说的“语素”)。例如**“整数”标记可以包含所有数字字符序列**。https://baike.baidu.com/item/词法分析器/4336210?fr=aladdin
  • 词法分析器的任务:字符流到记号流。
    • 字符流:和被编译语言密切相关(ASCII,Unicode,or……)
    • 记号流编译器内部定义的数据结构,编码所识别出的词法单元

2> 语法分析:

  • 输入是词法分析器返回记号流输出是语法树。
  • 目的是得到语言结构以树的形式表示
  • 对于声明性语句,进行符号表的查填
  • 对于可执行语句,检查结构合理的表达式运算是否有意义。

3> 语义分析:

  • 根据语义规则对语法树中的语法单元进行静态语义检查,如类型检查和转换等,目的在于保证语法正确的结构语义分析上也是合法的。

4> 中间代码生成(可选):

  • 生成一种既接近目标语言,又与具体机器无关的表示,便于代码优化与代码生成。

    (到目前为止,编译器与解释器可以一致)

5> 中间代码优化(可选):

  • 局部优化、循环优化、全局优化等;
  • 优化实际上是一个等价变换,变换前后的指令序列完成同样的功能,但在占用的空间上和程序执行的时间上都更省、更有效

6> 目标代码生成:

  • 不同形式的目标代码:汇编语言形式、可重定位二进制代码形式、内存形式(Load-and-Go)

7> 符号表管理:

  • 合理组织符号便于各阶段查找\填写等.

8> 出错处理:

  • 动态错误:源程序中的逻辑错误,发生在程序运行的时候。也称为动态语义错误

  • 静态错误:静态错误分为语法错误静态语义错误.

    • 语法错误:有关语言结构上的错误,如单词拼写错误、表达式缺少操作数、begin和end不匹配
    • 静态语义错误:分析源程序时可以发现的语言意义上的错误,如加法的两个操作数一个是整形变量,另一个是数组名

4)编译器的分析\综合模式

编译原理知识点_第4张图片

逻辑上把编译器分为分析(前端)部分综合(后端)部分

  • 分析(前端)语言结构和意义的分析; 从词法分析到中间代码生成各阶段的工作
  • 综合(后端)语言意义处理;从中间代码生成到目标代码生成的各阶段的工作
  • 编译器和解释器的区别往往是在形成**中间代码之后(处理阶段)**开始的。

5. 编译器扫描的遍数

​ 每个阶段将程序完整分析一遍的工作模式称为一遍扫描
(将源程序或源程序的某种形式的中间表示完整分析一遍,亦称作一遍扫描)

第二章 词法分析

1. 词法分析中的若干问题

1) 记号、模式与单词

单词的分类:关键字(保留字)、标识符、字面量、特殊符号
模式(pattern)产生/识别单词的规则
记号(token):按照
某个模式
(或规则)识别出的元素(一组)
单词(lexeme):被识别出的元素的值(字符串本身) ,也称为词值

2) 词法分析器的作用与工作方式

词法分析器的作用:

  • 1>识别记号并交给语法分析器(根据模式识别记号)
  • 2> 滤掉源程序中的无用成分, 如注释、空格和回车等。
  • 3> 处理与具体平台有关的输入(如文件结束符的不同表示等)
  • 4> 调用符号表管理器出错处理器,进行相关处理

工作方式:

  • 1.单独一遍扫描
  • 2.作为语法分析器的子程序
  • 3.并行方式

2. 模式的形式化描述

1) 字符串与语言

语言L有限字母表∑有限长度字符串的集合
定义中强调两个有限,因为计算机的表示能力有限 :

  • 字母表是有限的,即字母表中元素是有限多个;
  • 字符串的长度是有限的,即字符串中字符个数是有限多个。
    • 字符串与字符串集合相关的概念与运算:如前缀、后缀、子串、子序列
    • 字符串的并、交、连接、差、闭包

2) 正规式与正规集

正规式又称为正则表达式

​ 令Σ是一个有限字母表,则Σ上的 正规式 及其表示的集合递归定义如下:

  • 1> ε是正规式,它表示集合 L(ε) = {ε}

  • 2>若a是Σ上的字符,则a是正规式,它表示集合L(a)={a}

  • 若正规式r和s分别表示集合L®和L(s),则

    • (a) r|s是正规式,表示集合L®∪L(s)
    • (b) rs是正规式,表示集合L®L(s)
    • (c) r是正规式,表示集合
    • (d)®是正规式,表示的集合仍然是

    括弧用来改变运算的先后次序!

可用正规式描述(其结构)的语言称为 正规语言正规集

​ 我的理解正规式是一个式子,它表示的是在该规则,式子下的字符组合的集合

  • 能用正规表达式正规文法表示的集合称为正规集。https://www.baidu.com/link?url=wGgmaZfpiFULEpkBsNRtXb42J6kQuXRS1FKD6B87FMM-8C4gRCPA3QIamrRdWplfP_-4JiAOXhvxzv-D_WVWH5QYoJ2DemNNHjI9EZ_C7V3&wd=&eqid=ea75c2f800042099000000065eb78331
正规式 正规集
a {a}
a|b {a,b}
ab {ab}
(a|b)* {ε,a,b,aa,ab,ba,bb,aaa,……}

若运算的优先级和结合性做下述约定:

  • 三种运算均具有左结合性质
  • 优先级从高到低顺序排列为:闭包运算、连接运算、或运算

则正规式中不必要的括号可以被省略。

正规式P和Q表示了同一个正规集,则称P和Q是等价的,记为P=Q

编译原理知识点_第5张图片

3) 简化正规式描述

  • 正闭包

    • 若r是表示L®的正规式,则r+是表示(L®)+的正规式,且下述等式成立:r+ = rr = rr,r = r+|ε;*
    • +与具有相同的运算结合性和优先级*
  • 可缺省

    • 若r是正规式,则r?是表示L®∪{ε}的正规式,且下述等式成立:r? = r|ε
    • ? 与 * 具有相同的运算结合性和优先级
    • 若r是若干字符进行连接运算构成的正规式,则:串“r” = r ,且: ε= “”, a = “a”(a是Σ的任一字符)
  • 字符组

    • 若r是若干字符进行|运算构成的正规式,则可改写为 [r’],其中r’可以有如下两种书写形式:
      • 枚举:a|b|e|h,可写为 [abeh]
      • 分段:0|1|2|3|4|5|6|7|8|9|a|b|c|d|e , 可写为: [0-9a-e]
  • 非字符组

    若[r]是一个字符组形式的正规式,则[^r]是表示∑- L([r])的正规式。

    编译原理知识点_第6张图片

3. 记号的识别——有限自动机

1) 不确定的有限自动机

  • NFA(NondeterministicFinite Automaton, NFA)是一个五元组(5-tuple)M =(S,∑,move,s0,F),其中

    • S是**有限个状态(state)**的集合;
    • ∑是有限个输入字符(包括ε)的集合;
    • move是一个状态转移函数。eg:move(si,ch)=sj表示:当前状态si下若遇到输入字符ch,则转移到状态sj;
    • s0是唯一的初态(也称开始状态);
    • F是终态集(也称接受状态集),它是S的子集,包含了所有的终态
  • 直观的表示方式

    • 状态转换图:用一个有向图来直观表示NFA
    • 状态转换矩阵:用一个矩阵来直观表示NFA (矩阵中,状态对应行字符对应列)
  • NFA(识别记号)的特点

    NFA识别记号的最大特点是它的不确定性,即在当前状态下对同一字符有多于一个的下一状态转移。

    具体体现:

    • 定义: move函数是1对多的;
    • 状态转换图:从同一状态出发,可通过多于一条标记相同字符的边转移到不同的状态;
    • 状态转换矩阵M[si,a]是一个状态的集合

  • NFA识别记号存在的问题

    • 只有尝试了全部可能的路径,才能确定一个输入序列不被接受,而这些路径的条数随着路径长度的增长成指数增长
    • 识别过程中需要进行大量回溯,时间复杂度升高且算法复杂

2) 确定的有限自动机

  • 定义: DFA(Deterministic Finite Automaton, DFA))是NFA的一个特例,其中:
    • 没有状态具有ε状态转移(ε-transition),即状态转换图中没有标记ε的边
    • 每个状态s和每个字符a最多有一个下一状态
  • 特点:与NFA相比,DFA的特征:确定性
  • 定义: move(si, a) 函数都是 1对1 的;
    • 状态转换图: 从一个状态出发的任2条边上的标记均不同;
    • 状态转换矩阵M[si,a]是一个状态 且字母表不包括ε。

提示:正规式和有限自动机从两个侧面表示正规式正规式是描述,自动机是识别

3)DFA和NFA的区别

  • DFA:只有唯一初态NFA:有初态集
  • DFA是NFA的特例。
  • 对于每个NFA M,存在一个DFA M’ ,使得L(M)=L(M’ )
  • 对于任何两个有穷自动机M和M’ ,如果L(M)=L(M ’ ),则称M与M’ 是等价的。

4. 从正规式到词法分析器

构造词法分析器的一般方法和步骤:参考https://blog.csdn.net/qq_40427276/article/details/102771393编译原理知识点_第7张图片

  1. 用正规式描述模式(为记号设计正规式);
  2. 每个正规式构造一个NFA,它识别正规式所表示的正规集;
  3. 将构造的NFA转换成等价的DFA,这一过程也被称为确定化
  4. 优化DFA,使其状态数最少,这一过程也被称为最小化
  5. 根据优化后的DFA构造词法分析器

1) 从正规式到NFA

Thompson 算法

编译原理知识点_第8张图片

三种基本状态图

  • r=s|t

编译原理知识点_第9张图片

  • r=st

在这里插入图片描述

  • r=s*

编译原理知识点_第10张图片

举个栗子:

编译原理知识点_第11张图片

2) 从NFA到DFA

  • smove(S, a):从状态集S出发,标记为a的下一状态全体。与move(s, a)的唯一区别:用状态集取代状态
  • ε-闭包(T): 从状态集T出发,不经任何字符达到的状态全体
  • 每一个子集看成一个状态,“子集法”构造DFA

编译原理知识点_第12张图片

编译原理知识点_第13张图片

备注:

  • 第一个状态I需要填入初始态X,其他不用

  • 若2为某个状态S,则在求Ia时**先加入e(空)**求能到达的状态,再加入字符a,求能到达的状态,合并即为Ia此时的状态。

    eg {1,2,4,5,6,Y}走e(空)可到1,2,6,Y走a,2到4,6到6,不能到3,也不能到5,所以新状态为**{1,2,3,6,Y}**

  • 通过状态转换矩阵画出DFA状态转换图

编译原理知识点_第14张图片

备注:

  • 注意哪个状态是初态,哪个是终态
  • 初态是ε-closure({X})
  • 终态是含有原终态Y的子集

3) 最小化DFA

主要步骤:

  • 1.初始划分:终态组 , 非终态组
  • 2.利用可区分的概念,反复分裂划分中的组Gi,直到不可再分裂;
  • 3.由最终划分构造D’,关键是选代表和修改状态转移;
  • 4.消除可能的死状态和不可达状态。

编译原理知识点_第15张图片

编译原理知识点_第16张图片

编译原理知识点_第17张图片

5. 从DFA构造词法分析器

分类: 表驱动型的词法分析器;直接编码的词法分析器
比较:

表驱动 直接编码
分析器的速度
程序与模式的关系 无关 有关
适合的编写方法 工具生成 手工编写
分析器的规模 较大 较小

第三章 语法分析

词法分析: 记号的集合,字符串由字母组成,线性结构
语法分析: 句子的集合,句子由记号组成,非线性结构(树)

语法分析的双重含义:

  • 语法规则:上下文无关文法(子集:LL文法或LR文法
  • 语法分析:下推自动机(LL或LR分析器)、自上而下分析、自下而上分析

1. 语法分析的若干问题

许多编译器,特别是由自动生成工具构造的编译器,往往其前端的中心部件就是语法分析器

1)语法分析器的作用

  • 根据词法分析器提供的记号流,为语法正确的输入构造分析树(或语法树)
  • 检查输入中的语法(可能包括词法)错误,并调用出错处理器进行适当处理

编译原理知识点_第18张图片

2)语法错误的处理原则

源程序中可能出现的错误

  • 语法(包括词法)错误语义错误(静态语义错误和动态语义错误)

    注:跟第一章的分类角度不同,第一章是从静态错误(语法错误,静态语义错误)和动态错误(动态语义错误)分类的,但是殊途同归。

  • 词法错误:非法字符或拼写错关键字、标识符

  • 语法错误: 指语法结构出错,如少分号、括号不匹配、begin/end不配对

  • 静态语义错误: 如类型不一致、参数不匹配等

  • 动态语义错误(逻辑错误):死循环、变量为零时作除数等

2. 上下文无关文法(CFG)

1)上下文无关文法

CFG(Context Free Grammar,CFG)是一个四元组G =(N,T,P,S),其中

  • N是非终结符(Nonterminals)的有限集合
  • T是终结符(Terminals)的有限集合,且N∩T=Φ
  • P是产生式(Productions)的有限集合,A→α,其中A∈N(左部)α∈(N∪T)*(右部),若α=ε,则称A→ε为空产生式(也可以记为A →);
  • S是非终结符,称为文法的开始符号(Start symbol)

注: S ∈ N , N可以出现在产生式左边和右边,T绝不出现在产生式左边.

2)CFG产生语言的基本方法-推导

CFG(产生式)通过推导的方法产生语言,即(通俗地讲)从开始符号S开始,反复使用产生式:将产生式左部的非终结符替换为右部的文法符号序列(展开产生式,用=>表示),直到得到一个终结符序列

  • 1> 直接推导:利用产生式产生句子的过程中,将用产生式A→γ的右部代替文法符号序列αAβ中的A得到αγβ的过程,称αAβ直接推导出αγβ,记作:αAβ=>αγβ
  • 2> 零步或多步推导:若对于任意文法符号序列α1,α2,…αn,有α1=>α2=>…=>αn,则称此过程为零步或多步推导,记为:α1 =*> αn,其中α1=αn的情况为零步推导。
  • 3> 至少一次推导:α1≠αn,即推导过程中至少使用一次产生式 ,则称此过程为至少一步推导,记为:α1 =+> αn

(推导具有自反性和传递性)

  • 4> 由 CFG所产生的**语言L(G)*被定义为: L(G) = { ω┃S ω and ω∈T },

    • L(G)称为上下文无关语言(Context Free Language, CFL),ω称为句子
    • S =* > α,α∈(N∪T)*,则称α为G的一个句型句子一定是句型,反之不是。
  • 5> 在推导过程中,若每次直接推导均替换句型中最左边的非终结符,则称为最左推导,由最左推导产生的句型被称为左句型。 类似的可以定义最右推导与右句型,最右推导也被称为规范推导

    • 举一个小栗子https://blog.csdn.net/Jane_96/article/details/80024154

      文法G(E)为:

      E -> T | E + T |E - T
      T -> F | T * F | T / F
      F -> (E) | i
      给出i + i * i 的最左推导和最右推导。(注意集合的运算)

    • 最左推导:

      从文法的起始符号开始:
      E -> E + T
      -> T + T
      -> F + T
      -> i + T
      -> i + T * F
      -> i + F * F
      -> i + i * F
      -> i + i * i

    • 最右推导

      从文法的开始符号开始:
      E -> E + T
      -> E + T * F
      -> E + T * i
      -> E + F * i
      -> E + i * i
      -> T + i * i
      -> F + i * i
      -> i + i * i

3)推导、分析树与语法树

  • 分析树既反映语言结构的实质,也反映推导过程
  • 对CFG G的句型,分析树被定义为具有下述性质的一棵树。
    • 1) 根由开始符号所标记;
    • 2) 每个叶子由一个终结符、非终结符、或ε标记
    • 3) 每个内部结点由一个非终结符标记;
    • 4) 若A是某内部节点的标记,且X1,X2,…,Xn是该节点从左到右所有孩子的标记,则A→X1X2…Xn是一个产生式。若A→ε,则标记为A的结点可以仅有一个标记为ε的孩子。

注:分析树的叶子,从左到右构成G的一个句型。若叶子仅由终结符标记,则构成一个句子

  • 对CFG G的句型,表达式的语法树被定义为具有下述性质的一棵树:

    • 1) 内部节点由表达式中的操作符标记
    • 2) 叶子由表达式中的操作数标记;
    • 3)用于改变运算优先级和结合性的括号,被隐含在语法树的结构中。
  • 语法树是表示表达式结构的最好形式

  • 语法树和分析树的区别:

    • 语法分析的结果表示为分析树(parse tree)或语法树(syntax tree)。

    • 语法树是分析树中所包含信息的浓缩(有时因为语法树表示从分析树中的进一步抽取,所以也被称为抽象的语法树(abstract syntax tree))

    • 举个栗子:https://www.cnblogs.com/xiaomiao/p/3146390.html

      编译原理知识点_第19张图片

      分析树:

      编译原理知识点_第20张图片

      语法树:

      编译原理知识点_第21张图片

4)二义性与二义性的消除

二义性:若文法G对 同 一句子产生不止一棵分析树,则称G是二义的.

举个栗子:

如果一个文法中存在**某个句子(**全部由终结符组成的句型)对应两棵不同的语法树,则该文法具有二义性,如文法:

G(E):
	E->i
	E->E+E
	E->E*E

存在句子i+i*i,可以对应这样两棵语法树:
编译原理知识点_第22张图片编译原理知识点_第23张图片
是具有二义性的。

或者在填写分析表(下面会讲到)时,某个单元有两个产生式。https://www.bilibili.com/video/BV19J411B7ha?p=85

编译原理知识点_第24张图片

结论:

  • 1> 一个句子有多于一棵分析树,仅与文法和句子有关与采用的推导方法无关
  • 2> 造成文法二义的根本原因:文法中缺少对文法符号优先级和结合性的规定

二义性消除的方法:
① 改写二义文法为非二义文法
规定二义文法中符号的优先级和结合性,使仅产生一棵分析树。

3. 语法与文法简介

1)正规式与上下文无关文法

  • 记号可以用正规式描述,正规式适合描述线性结构,如标识符、关键字、注释等.
  • 句子可以用CFG描述,CFG适合描述具有嵌套(层次)性质的非线性结构,如不同结构的句子if-then-else\while-do等

正规式(1型文法)所描述的语言结构均可以用CFG(2型文法)描述,反之不一定.

2)上下文有关文法CSG

典型的这类语言结构包含:计数问题的抽象、变量的声明与引用、过程调用时形参与实参的一致性检查等。描述它们的文法被称为上下文有关文法(Context Sensitive Grammar,CSG)。这些语言结构无法用上下文无关文法CFG来描述.

3)文法类型

若文法G=(N,T,P,S)的每个产生式α→β中,均有α∈(N∪T),且至少含有一个非终结符*,β∈(N∪T)*,则称G为0型文法.

0型文法施加以下第i条限制,即得到i型文法

1> G的任何产生式α→β(S→ε除外)满足|α|≤|β|;
2> G的任何产生式形如A→β,其中A∈N,β∈(N∪T)*;
3> G的任何产生式形如A→a或者A→aB(或者A→Ba),其中A和B∈N,a∈T。

文法 语言 自动机
短语文法(0型) 短语结构语言 图灵机
CSG(1型) CSL 线性界线自动机
CFG(2型) CFL 下推自动机
正规文法(3型) 正规集 有限自动机

举个栗子:https://www.cnblogs.com/ITGirl00/p/3391211.html

S-> aaS|a是什么型的,为什么
S-> aSb|ab是什么型的,为什么
S-> SaS|b是什么型的,为什么

  • 从0型到3型,其规则和约定越来越多,限制条件也越来越多,所以,我们判断时可以从最复杂的3型进行判断,依次向下判断,如果不符合3型的,那再看是不是2型的,不是2型的,再看是不是1型的,当然,对于作题作的熟的朋友,不用这么复杂,可以一眼直接看出来。
  • 3型文法(正规文法)遵循什么规范呢?
    • 第一点:左边必须只有一个字符,且必须是非终结符
    • 第二点:其右边最多只能有两个字符,且当有两个字符时必须有一个为终结符而另一个为非终结符。当右边只有一个字符时,此字符必须为终结符。
    • 第三点:对于3型文法中的所有产生式,其右边有两个字符的产生式,这些产生式右边两个字符中终结符和非终结符的相对位置一定要固定,也就是说如果一个产生式右边的两个字符的排列是:终结符+非终结符,那么所有产生式右边只要有两个字符的,都必须前面是终结符而后面是非终结符。反之亦然,要么,就全是:非终结符+终结符
  • 2型文法(上下文无关文法)如何判断:
    • 第一点:与3型文法的第一点相同,即:左边必须有且仅有一个非终结符。
    • 第二点:2型文法所有产生式的右边可以含有若干个终结符和非终结符(只要是有限的就行,没有个数限制)。
    • 为什么“上下文无关“,因为左边当且仅当只有一个非终结符
  • 1型文法(上下文有关文法)如何判断:
    • 第一点:1型文法所有产生式左边可以含有一个、两个或两个以上的字符,但其中必须至少有一个非终结符
    • 第二点:与2型文法第二点相同。
    • 为什么“上下文有关”,因为左边不止一个符号,eg:aSb -> aaSbb,此时S有正确的“上下文”
  • 最后是0型文法,这个就不用看了,只要你能描述出来,都属于这个类型,即0型。

所以,取其最高的符合规则,最后的答案是其符合:上下文无关文法规则,即2型

备注:

  • 3型文法是2型文法的特殊情况。

4. 自上而下语法分析

分为:递归下降分析法预测分析法

基本思想:对任何一个输入序列ω,从S开始进行最左推导,直到得到一个合法的句子或发现一个非法结构。整个自上而下分析是一个试探的过程,是反复使用不同产生式谋求与输入序列匹配的过程。

提前准备——重写文法:1.消除左递归,以避免陷入死循环; 2.提取左因子,以避免回溯。

1)消除左递归,变成右递归文法

​ 定义:若文法G中的非终结符A,对某个文法符号序列α存在推导A =+> Aα,则称G是左递归的。若G中有形如A→Aα的产生式,则称该产生式对A直接左递归

1.1)消除直接左递归

<1> 消除文法的直接左递归,变成直接右递归

A→Aα|β     替换为     A →βA'
                    A'→αA'|ε
  • 首先,整理A产生式为如下形式:A→ Aα1|Aα2|…|Aαm|β1|β2|…|βn
  • 然后用下述产生式代替A产生式:A→ β1 A’|β2 A’| …|βn A’

​ A’→ α1 A’ | α2 A’ | … | αm A’ |ε

  • 为什么替换后的右递归和左递归是等价的?

    左递归: A -> Aα -> Aαα _> … -> Aαα…α ->βαα…α

    右递归: A -> βA’ -> βαA’ -> βαα…A’ -> βαα…α

    所以两者是等价的,产生的语言是一样的

  • 举个栗子:

    文法G(E):

    E -> E+T|T

    T -> T*F|F

    F -> (E)|i

    消除直接左递归后(E最终会以T开头):

    E -> TE’

    E‘ -> +TE’|ε

    T -> FT’

    *T’ -> FT’|ε

    F -> (E)|i

1.2)消除左递归
  • 什么是左递归?

    举个栗子:

    文法G(S):

    S -> Qc|c

    Q -> Rb|b

    R -> Sa|a

    虽没有直接左递归,但存在S推导到以S为开头的串,S -> Qc -> Rbc -> Sabc。并且显然,S,Q,R都是左递归的

  • 一个文法消除左递归的条件

    • 不含以ε为右部的产生式
    • 不含回路:P =+> P为回路
  • 消除左递归的方法:反复替换,打断循环,变成直接左递归

编译原理知识点_第25张图片

1.3)FIRST集合

https://www.bilibili.com/video/BV19J411B7ha?p=67

终结首符集FIRST(α)

  • αi推导的所有以a开头的字符串的集合
  • 如果A的所有候选首符集两两不相交,则FIRST(αi) ∩ FIRST(αj) = Ø
  • 如果候选首符集存在相交,则需要反复提取左因子,使其变成两两不相交
    编译原理知识点_第26张图片
    编译原理知识点_第27张图片
1.4)如何计算FIRST(α)?

https://blog.csdn.net/jane_96/article/details/79888680

计算各个文法符号X的FIRST(X)时,不断应用下列规则,直到再没有新的终结符号或者ε可以被加入到任何FIRST集合中为止。

  • 如果X是一个终结符号,那么FIRST(X) = X。
  • 如果X是一个非终结符号,且X -> Y1Y2 …Yk是一个产生式,其中k ≥ 1,那么如果对于某个i , a 在FIRST(Yi)中且ε在所有的FIRST(Y1)、FIRST(Y2)、….、FIRST(Yi-1)中,就把a加入到FIRST(X)中。也就是说,Y1…Yi-1 =>* ε。如果多于所有的j = 1,2,3,…,k , ε在FIRST(Yj)中,那么将 ε 加入到FIRST(X)中。比如,FIRST(Y1)中的所有符号一定在FIRST(X)中。如果Y1 不能推导出 ε ,那么,我们就不会再向FIRST(X)中加入任何符号,但是如果Y1 =>* ε ,那么我们就加上FIRST(Y2),以此类推。
  • 如果X -> ε 是一个产生式,那么将ε 加入到**FIRST(X)**中。

举个栗子:

文法G[E]: 
1. E -> TE’ 
2. E’-> +TE’|ε
3. T -> FT’ 
4. T’-> *FT’|ε
5. F -> (E)|a 
-------
FIRST(E): 由E -> TE’可得FIRST(E) = FIRST(T) 
FIRST(T): 由T -> FT’可得FIRST(T) = FIRST(F) 
FIRST(F): 由F -> (E)和F -> a可得FIRST(F) = { ( , a }; 
FIRST(T’): 由T’-> *FT’和T’-> ε 可得FIRST(T’) = { * , ε }; 
FIRST(E’): 由E’-> +TE’和E’-> ε可得FIRST(E’) = { + , ε };
------
FIRST(E) = { ( , a }; 
FIRST(T) = { ( , a }; 
FIRST(F) = { ( , a }; 
FIRST(T’) = { * , ε }; 
FIRST(E’) = { + , ε };

如何根据α定义的终结首符集FIRST(α)自顶向下匹配句子。(此时还没有引入FOLLOW集)

编译原理知识点_第28张图片

2)提取左因子

2.1)提取文法的左因子

左因子产生原因:公共前缀:A → αβ1|αβ2
方法:将 A → αβ1|αβ2|γ
替换为 A→αA’|γ A’→β1|β2

2.2)FOLLOW集合

https://www.bilibili.com/video/BV19J411B7ha?p=68

  • FOLLOW(A) = {a|S => …Aa…,a是终结符}
  • 若A为句子尾部,则a为#

编译原理知识点_第29张图片

2.3)如何计算FOLLOW(A)

https://blog.csdn.net/jane_96/article/details/79888680

计算所有非终结符号A的FOLLOW(A)集合时,不断应用下列规则,直到再没有新的终结符号可以被加入到任意FOLLOW集合中为止。

  • 将 $ 放到FOLLOW(S)中,其中S是开始符号,而**$ 是输入右端的结束标记**。
  • 如果存在一个产生式 A -> αBβ , 那么FIRST(β)中除 ε之外的所有符号都在**FOLLOW(B)**中。
  • 如果存在一个产生式A -> αB,或存在产生式 A -> αBβ 且 FIRST(β)包含 ε ,那么**FOLLOW(A)中的所有符号加入FOLLOW(B)**中。

举个栗子:

https://blog.csdn.net/qq_40294512/article/details/89397905

E -> T E'
E' -> + T E' | ε
T -> F T'
T' -> * F T' | ε
F -> ( E ) | id
------
FIRST(E) = FIRST(T) = FIRST(F) = { ( , id }
FIRST(E') = FIRST(+) ∪ FIRST(ε)= { + ,  ε }
FIRST(T) = FIRST(F) = {( , id}
FIRST(T') = FIRST(*) ∪ FIRST(ε)= { * ,  ε }
FIRST(F) = {( , id}
-----
FOLLOW(E) ,根据规则1,首先把$加入进来,根据规则2,可以得出 FOLLOW(E) = { ) , $ }
FOLLOW(E’) = FOLLOW(E) = { ) , $ } 根据规则3
FOLLOW(T) = FIRST(E’) ∪ FOLLOW(E) = { + , ) , $ } 根据规则2
FOLLOW(T’) = FOLLOW(T) = { + , ) , $ } 根据规则3
FOLLOW(F) = FOLLOW(T) ∪ FIRST(T’) = { * , + , ) , $ } 根据规则2和3

FOLLOW(E) = FOLLOW(E') = { ) , $ }
FOLLOW(T) = FOLLOW(T') = { + , ) , $ }
FOLLOW(F) = { * , + , ) , $ }

备注:

  • 对于非终结符B求FOLLOW

    • B都在产生式右边,主要看B是否可能在右边的最尾部,如果存在,则需要考虑产生式左边的FOLLOW,如果没有,则考虑紧跟其后的字符的FIRST即可

    • 原则1:若B在右边,则B依赖紧跟其后的字符C,找字符C的FIRST©,并去掉 ε ,加入FOLLOW(B)

    • 原则2:若B在右边且为倒数第二个字符,如果倒数第一个字符可为空,则B依赖于左边非终结符A,找FOLLOW(A加入FOLLOW(B)

  • 所有非终结符的FOLLOW集合同时初始化为空集合,并行填写FOLLOW集合

  • 一个产生式可能会匹配多个原则

  • ε永远不进FOLLOW集合

2.4)LL(1)文法

https://www.bilibili.com/video/BV1tt41177b5?p=14

构造不带回溯的LL(1)文法条件:

  • 处理左递归,再处理左因子,保证候选首符集不存在相交,推导时不存在冲突
  • 保证FIRST集合和FOLLOW集合不相交,推导不存在冲突
  • 为什么叫LL(1)文法?
    • 第一个L:从左到右扫描输入串
    • 第二个L:最左推导
    • **1:**分析时每一步只需向前查看一个符号

编译原理知识点_第30张图片

2.5)LL(1)分析法

在这里插入图片描述

3)递归下降分析

直接以程序代码(的方式)模拟产生式产生语言的过程:

基本思想: 每个非终结符对应一个子程序(函数),过程体中:

  • 产生式右部的非终结符:对应子程序调用
  • 产生式右部的终结符与输入记号序列进行匹配
  • 几个全局过程和变量https://www.bilibili.com/video/BV19J411B7ha?p=75
    • ADVANCE:把输入串指示器IP指向下一个输入符号
    • **SYM:**IP当前所指的输入符号
    • ERROR:出错处理子程序

编译原理知识点_第31张图片

特点:

  • 1> 子程序是递归的(因为文法是递归的);

  • 2> 程序与文法相关;

  • 3> 它对文法的限制是不能有公共左因子和左递归;

  • 4> 它是一种非形式化的方法,只要能写出子程序,用什么样的方法和步骤均可。

4)预测分析程序(器)

4.1)预测分析程序的构成:

https://www.bilibili.com/video/BV19J411B7ha?p=81

  • 总控程序: 根据现行栈顶符号当前输入符号,执行动作

  • 分析表M[A, a]矩阵: A∈N,a∈T是终结符或“#”,是一种数据结构

  • 分析栈: 用来存放文法符号

编译原理知识点_第32张图片

4.2)预测分析过程

编译原理知识点_第33张图片

4.3)预测分析示例

编译原理知识点_第34张图片

备注:

  • 行表示非终结符,列表示终结符。
  • 对于句子匹配,可以通过分析表可以选择正确候选
4.4)如何填写分析表

分析表是矩阵M[A,a],是一种数据结构。根据FIRST集合和FOLLOW集合

  • 对于A -> α(不为空),则通过FIRST集填入分析表
  • 对于A -> ε,则通过FOLLOW集填入分析表
    编译原理知识点_第35张图片
    编译原理知识点_第36张图片

☆ 无论是递归下降子程序法还是非递归的预测分析法,他们都只能处理LL(1)文法

5. 自下而上语法分析

自上而下:从开始符号出发,根据产生式规则推导给定的句子。用的是推导。

自下而上: 从给定的句子规约到文法的开始符号。用的是归约(规范归约—剪句柄—移进/归约分析—SLR(1)分析器).

1)自下而上分析的基本思想

​ 主要有算符优先分析法LR分析法

1.1)基本思想和基本概念(短语,直接短语,句柄)
  • 基本思想: 最左归约。

对于每个输入序列ω:从左到右扫描ω; 从ω开始,反复用产生式的左部替换产生式的右部(即当前句型中的句柄)、谋求对ω的匹配,最终得到文法的开始符号,或者发现一个错误。

  • 基本概念:

    • a> 设αβδ文法G的一个句型,若存在S=*>αAδ,A=+>β, 则称β是句型αβδ相对于A的"短语"

      特别的,若 有A→β,则 称β是句型αβδ相对于产生式A→β的"直接短语".
      一个句型的最左直接短语 被称为 “句柄”.

      特征:

      • 短语: 以非终结符为根子树中所有从左到右的叶子;

      • 直接短语:只有父子关系的子树中所有从左到右排列的叶子(树高为2);

      • 句柄: 最左边父子关系树中所有从左到右排列的叶子(句柄是唯一的)

        如果觉得定义抽象,可参考该博客https://blog.csdn.net/it_dream_er/article/details/53612006
        在这里插入图片描述编译原理知识点_第37张图片

1.2)移进-规约

a> 移进-规约的例子

1.已知文法:

E→E+T | T

T→T*F | F

F→(E) | i

以句柄作为可归约串,写出符号串‘i+i*i#’的"移进-归约"分析过程。

符号栈 输入串 动作
# i+i*i# 移进
#i +i*i# 归约
#F +i*i# 归约
#T +i*i# 归约
#E +i*i# 移进
#E+ i*i# 移进
#E+i *i# 归约
#E+F *i# 归约
#E+T *i# 移进
#E+T* i# 移进
#E+T*i # 归约
#E+T*F # 归约
#E+T # 归约
#E # 接受

b>最左归约:若 α是文法G的句子且满足下述条件,则称序列αn,αn-1,…,α0是α的一个最左归约。

    1. αn = α
    1. α0 = S(S是G 的开始符号)
    1. 对任何i(0

    最左归约的逆过程是一个最右推导,分别称最右推导和最左归约为规范推导和规范归约.

c)移进-归约分析器

  • 工作方式:格局与格局变换

  • 分析表

  • 驱动器(模拟算法)

  • SLR分析表的构造

  • LR(文法、语言、分析器)

  • 改变格局的动作:

    • 移进(shift):当前剩余输入的下一终结符进栈。
    • 归约(reduce):将栈顶句柄替换为对应非终结符(最左归约)
    • 接受(accept):宣告分析成功
    • 报错(error):发现语法错误,调用错误恢复例程

编译原理知识点_第38张图片

2)算符优先分析法

2.1)什么是算符优先文法

​ 按照算符的优先关系结合性质进行语法分析;适合分析表达式。
编译原理知识点_第39张图片
编译原理知识点_第40张图片

备注:

  • 如果表格单元中没有重复项,则为算符优先文法
2.2)FIRSTVT集合和LASTVT集合

FIRSTVT§:P产生式右端从左往右第一个终结符

LASTVT§:P产生式右端从右往左第一个终结符
编译原理知识点_第41张图片
编译原理知识点_第42张图片
​ FIRSTVT集合和LASTVT集合的目的是为了求出字符之间的优先级,构造优先关系表

2.3)FIRSTVT的计算

编译原理知识点_第43张图片
编译原理知识点_第44张图片
编译原理知识点_第45张图片
备注:

  • FIRSTVT§:P产生式右端从左往右第一个终结符
  • 注意要反复利用规则计算
2.4)LASTVT的计算

编译原理知识点_第46张图片编译原理知识点_第47张图片
编译原理知识点_第48张图片
备注:

  • LASTVT§:P产生式右端从右往左第一个终结符
  • 注意要反复利用规则计算
2.5)构造优先关系表

编译原理知识点_第49张图片
先看产生式(1)右端 “+“的前面字符:LASTVT(E) > “+” ,及左边算符 >右边算符。
编译原理知识点_第50张图片

再看产生式(1)右端 “+“的后面字符:"+" < FIRSTVT(E) ,及左边算符 < 右边算符。
编译原理知识点_第51张图片
编译原理知识点_第52张图片

备注:

  • 左"(" = 右")",左"#" = 右"#"
  • 对于"#"的优先级,考虑句子"#E#"
    • 对于左边"#","#" < FIRSTVT(E)
    • 对于右边"#",LASTVT(E) > “#”

3)LR分析法

​ 规范规约:句柄作为可规约串;寻找句柄是关键。如果句柄入栈,则需要马上规约,否则会错过规约的时机。

编译原理知识点_第53张图片
​ LR分析法中,L是从左到右扫描输入串,R是指自下而上进行规约。

3.1)LR分析法框架

编译原理知识点_第54张图片
编译原理知识点_第55张图片

3.2)LR分析表概述
  • 移进(shift):当前剩余输入的下一终结符进栈。编译原理知识点_第56张图片

  • 归约(reduce):将栈顶句柄替换为对应非终结符(最左归约)编译原理知识点_第57张图片

  • 接受(accept):宣告分析成功
    编译原理知识点_第58张图片

  • 报错(error):发现语法错误,调用错误恢复例程
    编译原理知识点_第59张图片

3.3)LR分析过程

​ 用三元组:(状态栈,符号栈,输入串)来表示
编译原理知识点_第60张图片

如何查看LR分析表进行自下而上语法分析?

https://www.bilibili.com/video/BV12741147J3?p=107

编译原理知识点_第61张图片
编译原理知识点_第62张图片
编译原理知识点_第63张图片
编译原理知识点_第64张图片

备注:

  • 对于是移进还是规约,用状态栈顶的元素,以及输入串的第一个字符进行查ACTION表
  • 如果已经规约,用状态栈顶的元素 ,以及规约的字符进行查找GOTO表,并将状态压入状态栈
  • 对于LR(0)的GOTO表,有开始符号E。但对于SLR(1)的GOTO表,则没有开始符号S‘
  • 每次规约,拓展语法树。
3.4)前缀 & 活前缀

编译原理知识点_第65张图片

3.5)LR(0)分析表如何构造(拓广文法,LR(0)项目,NFA,DFA)

1>拓广文法
编译原理知识点_第66张图片

2>LR(0)项目
编译原理知识点_第67张图片

编译原理知识点_第68张图片

3> 构造识别文法所有活前缀的DFA

​ 先通过LR(0)项目构造NFA,然后通过合并相同的集合(变成族)构造DFA
编译原理知识点_第69张图片
编译原理知识点_第70张图片
编译原理知识点_第71张图片
备注:

  • NFA中双圈为规约态

  • 对于NFA,如果当前活前缀接下来扫描的字符为非终结符A,有两种动作

    • 走ε进A内部
    • 走A,完成对A的扫描。
  • 对于NFA变成DFA,不用像词法分析一样用子集法,用状态矩阵来转换,直接看图构造

    • 从初始态S‘开始,将相同的状态归于一族
    • 根据前一族中的活前缀,通过扫描不同的字符,依次向后构造新的活前缀,对于相同路径则归为一族。
    • 注意一些无限递归的状态。
  • ACTION和GOTO表中的状态其实是识别活前缀的DFA的状态

3.6)移进规约冲突& 规约规约冲突 & SLR(1)冲突解决方法
  • 每个移进规约冲突的项目集都存在两个动作

编译原理知识点_第72张图片

​ 如果一个项目集中,移进和规约的Follow集合相交非空,则可以确定是移进还是规约
编译原理知识点_第73张图片
编译原理知识点_第74张图片

备注:

  • 对于移进终结符,则直接看LR(0)有限自动机DFA即可(直观,其实用Folow集合看也行),FoLLow集主要是用来判断规约的,不是移进的。(在SLR(1)分析表构造中可以感受到)
  • 对于规约成非终结符,不同的Follow集合交集为空

SLR(1)冲突解决办法
编译原理知识点_第75张图片

备注:

  • SLR(1)可以解决规约规约冲突
  • 但是某些特殊情况,SLR(1)无法解决移进和规约的冲突(请见3.8)
3.7)SLR(1)分析表的构造

编译原理知识点_第76张图片
编译原理知识点_第77张图片
编译原理知识点_第78张图片
编译原理知识点_第79张图片
备注:

  • r1是指规约成产生式(1),而不是规约成E
3.8)SLR(1)存在的问题

解决了规约规约的冲突,但在某些情况下,移进规约会出现冲突
编译原理知识点_第80张图片
编译原理知识点_第81张图片

3.9)LR(1)分析表的构造

1>展望串
编译原理知识点_第82张图片

2>有效项目
编译原理知识点_第83张图片

3>如何构造LR(1)的DFA
编译原理知识点_第84张图片
编译原理知识点_第85张图片
编译原理知识点_第86张图片

备注:

  • 当前的产生式(假设左边为A)的展望串如何找的?需要通过规约成A回溯A之后的那个字符的FIRST集合,即为展望串。
  • 展望串的目的是用来确定哪个时候进行规约操作

编译原理知识点_第87张图片

编译原理知识点_第88张图片

3.10)LR(1)分析表的分析过程

编译原理知识点_第89张图片
编译原理知识点_第90张图片
备注:

  • 注意符号和状态是同时压入(弹出)栈的,符号个数和状态的个数是一致的。
  • r1是表示规约成第1个产生式。
  • 规约时需要先弹出栈顶元素(弹出多少,根规约的产生式而定),规约时利用栈顶元素的GOTO表确定规约后的状态。
3.11)LR(0), SLR(1), LALR(1), LR(1)对比与分析

​ 这些算法的演进主要是解决移进规约冲突的问题,请参考该博客LL(1),LR(0),SLR(1),LALR(1),LR(1)对比与分析

第四章 静态语义分析(待整理)

采用语法制导翻译生成中间代码

1. 语法制导翻译简介

(1)语法与语义的关系

语法是指语言的结构、即语言的“样子”;
语义是指附着于语言结构上的实际含意,即语言的“意义”.
一个语法上正确的句子,它所代表的意义并不一定正确.

语义分析的作用

• 检查结构正确的句子所表示的意思是否合法;
• 执行规定的语义动作,如:表达式求值、符号表的查询/填写、中间代码生成等

☆ 应用最广的语义分析方法是语法制导翻译,他的基本思想是将语言结构的语义以属性的形式赋予代表此结构的文法符号,而属性的计算以语义规则的形式赋予由文法符号组成的产生式.

(2)属性/语义规则的定义

定义4.1 对于产生式A→α,其中α是由文法符号X1X2...Xn组成的序列,它的语义规则可以表示为(4.1)所示关于属性的函数f:
          b := f(c1, c2, ..., ck)                  (4.1)
语义规则中的属性存在下述性质与关系:
      (1)   称(4.1)中属性b依赖于属性c1, c2, ..., ck。
      (2) 若b是A的属性,c1, c2, ..., ck是α中文法符号的属性,或者A的其它属性,则称b是A的综合属性。
      (3) 若b是α中某文法符号Xi的属性,c1, c2, ..., ck是A的属性,或者是α中其它文法符号的属性,则称b是Xi的继承属性。
      (4) 若语义规则的形式如下述(4.2),则可将其想像为产生式左部文法符号A的一个虚拟属性。属性之间的依赖关系,在虚拟属性上依然存在。
          f(c1, c2, ..., ck)                (4.2)          ■

☆ 继承属性从前辈和兄弟的属性计算得到,综合属性从子孙和自身的其他属性计算得到.

即,继承属性“自上而下,包括兄弟”,综合属性“自下而上,包括自身”.

(3)语义规则的两种形式

☆ 语义规则的两种形式(忽略实现细节,二者作用等价)

<1> 语法制导定义(Syntax Directed Definition)

用抽象的属性和运算表示的语义规则;(公式,做什么)

<2> 翻译方案(Translation Scheme)

用具体的属性和运算表示的语义规则。(程序段,如何做)

继承属性是自上而下计算的,综合属性是自下而上计算的.

(4)LR分析翻译方案的设计

☆ LR分析中的语法制导翻译实质上是对LR语法分析的扩充:

  1. 扩充LR分析器的功能

当执行归约产生式的动作时,也执行相应产生式对应的语义动作。由于是归约时执行语义动作,

​ 因此限制语义动作仅能放在产生式右部的最右边

  1. 扩充分析栈

​ 增加一个与分析栈并列的语义栈,用于存放分析栈中文法符号所对应的属性值

☆ 扩充后的LR分析最适合对综合属性的计算,而对于继承属性的计算还需要进行适当的处理.

2. 中间代码简介

☆ 中间代码应具备的特性
1)便于语法制导翻译
2)既与机器指令的结构相近,又与具体机器无关.

使用中间代码的好处:一是便于编译器程序的开发和移植,二是代码进行优化处理.

☆ 中间代码的主要形式:后缀式、树、三地址码等.最基本的中间代码形式是树;最常用的中间代码形式是三地址码,它的实现形式常采用四元式形式。

☆ 符号表是帮助声明语句实现存储空间分配的重要数据结构。

(1)后缀式

操作数在前,操作符紧随其后,无需用括号限制运算的优先级和结合性;便于求值.

(2)三地址码

① 三元式 形式: (i) (op, arg1, arg2)

​ 三地址码:(i):= arg1 op arg2

序号的双重含义:既代表此三元式,又代表三元式存放的结果

存放方式:数组结构,三元式在数组中的位置由下标决定

弱点:给代码的优化带来困难

② 四元式 形式: ( i ) (op,arg1,arg2,result)

​ 所表示的计算: result:= arg1 op arg2

  1. 四元式与三元式的唯一区别:将由序号所表示的运算结果改为:用(临时)变量来表示。
  2. 此改变使得四元式的运算结果与其在四元式序列中的位置无关.为代码的优化提供了极大方便,因为这样可以删除或移动四元式而不会影响运算结果.

③ 树形表示

1> 语法树真实反映句子结构,对语法树稍加修改(加入语义信息),即可以作为中间代码的一种形式(注释语法树)
2> 树的优化表示-DAG
3> 树与其他中间代码的关系

树表示的中间代码后缀式三地址码之间有内在联系

  1. 树 → 后缀式

​ 方法:对树进行深度优先后序遍历,得到的线性序列就是后缀式,或者说后缀式是树的一个线性化序列;

  1. 树 → 三元式/四元式

特点:树的每个非叶子节点和它的儿子对应一个三元式或四元式;

方法:对树的非叶子节点进行深度优先后序遍历,即得到一个三元式或四元式序列。

3. 符号表简介

  • 符号表的作用:连接声明与引用的桥梁,记住每个符号的相关信息,如作用域和类型等,帮助编译的各个阶段正确有效地工作。
  • **符号表的基本目标:**有效记录信息、快速准确查找。
  • 符号表设计的基本要求:
    • 正确存储各类信息;
    • 适应不同阶段的需求;
    • 便于有效地进行查找、插入、删除和修改等操作;
    • 空间可以动态扩充.

(1)构成名字的字符串

**构成名字的字符串的存储方式:**直接存储—定长数据(直接将构成名字的字符串放在符号表条目中)和间接存储—变长数据(将构成名字的字符串统一存放在一个大的连续空间内,字符串与字符串之间采用特殊的分隔符隔开,符号表条目中仅存放指向该字符串首字符的指针).

(2)名字的作用域

☆ 程序语言范围的划分可以有两种划分范围的方式:并列嵌套

☆ **名字的作用域规则:**规定一个名字在什么样的范围内应该表示什么意义.

<1> 静态作用域规则(static-scope rule):编译时就可以确定名字的作用域,即仅从静态读程序就可确定名字的作用域
<2> 最近嵌套规则(most closely nested):名字的声明在离其最近的内层起作用

(3)线性表

符号表以**栈(线性表)**的方式组织.

线性表上的操作:查找、插入、删除、修改

查找:从表头(栈顶)开始,遇到的第一个符合条件的名字;插入:先查找,再加入在表头(栈顶);

关键字 = 名字+作用域;

(4)散列表

名字挂在两个链上(便于删除操作):

  1. 散列链(hash link): 链接所有具有相同hash值的元素,表头在表头数组中;
  2. 作用域链(scope link):链接所有在同一作用域中的元素,表头在作用域表中.

☆ 操作:查找、插入、删除

4. 声明语句的翻译

(1)变量的声明

☆ 一个变量的声明应该由两部分来完成:类型的定义变量的声明

  • **类型定义:**为编译器提供存储空间大小的信息
  • **变量声明:**为变量分配存储空间
  • **组合数据的类型定义和变量声明:**定义与声明在一起,定义与声明分离.

1> 简单数据类型的存储空间是预先确定的,如int可以占4个字节,double可以占8个字节,char可以占1个字节等

2> 组合数据类型变量的存储空间,需要编译器根据程序员提供的信息计算而定.

(2) 过程

1.过程(procedure):过程头(做什么) +  过程体(怎么做);
   - 函数:  有返回值的过程
   - 主程序:  被操作系统调用的过程/函数

2.过程的三种形式:过程定义、过程声明和过程调用。
   过程定义:过程头+过程体;
   过程声明:过程头;

3. 左值与右值 
   1> 直观上,出现在赋值号左边和右边的量分别称为左值和右值;
   2> 实质上,左值必须具有存储空间,右值可以仅是一个值,而没有存储空间.
   3> 形象地讲,左值是容器,右值是内容.
   
4. 参数传递
   1> 形参与实参
        - 声明时的参数称为形参(parameter或formal parameter)
        - 引用时的参数称为实参(argument或actual parameter)
   2> 常见的参数传递形式:(不同的语言提供不同的形式)
        - 值调用(call by value)---过程内部对参数的修改,不影响作为实参的变量原来的值.
        - 引用调用(call by reference)--- 过程内部对形参的修改,实质上是对实参的修改.
        - 复写-恢复(copy-in/copy-out)--- ① 过程内对参数的修改不直接影响实参,避免了副作用;
                                        ② 返回时将形参内容恢复给实参,实现参数值的返回.
        - 换名调用(call by name)--- 宏调换
   3> 参数传递方法的本质区别: 实参是代表左值、右值、还是实参本身的正文.
   
5. 作用域信息的保存
☆ 能够画出嵌套过程的嵌套关系树(P191 4.33),根据语法制导翻译(P193 4.35)画出分析树,写出推导步骤,构造的符号表

5. 简单算术表达式与赋值句

P197 例4.36 主要是变量类型的转换

6. 数组元素的引用

(1)数组元素的地址计算

  • 注意是行主存储还是列主存储

(2)☆数组元素引用的语法制导翻译(考试热点之一)

  • P201 例4.37

7. 布尔表达式

布尔表达式的计算有两种方法:数值表示的直接计算和逻辑表示的短路计算

☆ 布尔表达式短路计算的翻译:短路计算的控制流,真出口与假出口,真出口链与假出口链,拉链回填技术(P207 例4.41)(考试热点之一)

8. 控制语句

**控制语句的分类:**①无条件转移、②条件转移、③循环语句、④分支语句

  • 无条件转移(goto)\条件转移(if、while)
  • 条件转移的语法制导翻译:P213 例4.42
多看课件PPT,多做题练手

你可能感兴趣的:(基础理论,#,编译原理)