一:基本概念
1. 计算机系统的语言
- 低级语言
- 机器语言:由0、1组成的机器指令序列
- 汇编语言:用符号表示的指令的集合
- 高级语言
- 程序设计语言:与自然语言接近
2. 语言处理程序
(1)定义
- 源程序:高级程序设计语言或汇编语言编写的程序
- 可执行程序:计算机理解的机器语言编写的程序
- 语言处理程序:将源程序翻译为可执行程序
(2)种类
- 汇编语言:需要汇编程序翻译
- 高级语言:需要解释程序或编译程序翻译
1:解释程序
- 直接解释并执行源程序
- 翻译为中间代码再执行
特点:
- 不生成独立的目标程序
- 需要反复扫描,效率低
- 灵活
- 可移植
2:编译程序
将源程序翻译成目标程序,并执行目标程序
特点:
- 生成独立保存的目标程序
- 效率高
- 不灵活
3. 程序设计语言的定义
- 语法:程序设计语言的基本符号组成各个语法成分的一组规则,用形式语言描述
- 词法规则:由基本字符构成的单词书写规则
- 语法规则:由符号构成语法成分的规则
- 语义:程序设计语言按语法规则构成的各个语法成分的含义,程序的执行结果说明了其语义
- 静态语义:编译时可以确定语法成分的含义
- 动态语义:运行时刻才能确定的语义
- 语用:构成语言的各个记号和使用者的关系
- 语境:理解和实现程序设计语言的环境
4. 程序设计语言的发展
- Fortran:第一个用来进行科学和工程计算的高级语言,解决数学公式的自然描述
- ALGOL
- PASCAL:过程式结构化语言
- C语言:通用程序设计语言,提供高效的执行语句,可以直接访问操作系统和底层硬件
- C++:面向对象,增加了封装、抽象、类
- C+:面向对象的高级程序设计语言
- Objective-C:面向对象,可以在gcc运作的系统中编译编写
- JAVA:通用程序设计语言
- Ruby:解释性、面向对象、动态类型的脚本语言,一切皆对象
- PHP:在服务器端运行的,嵌入HTML的脚本语言,充分利用服务器性能
- Python:面向对象的解释型语言,支持对操作系统的底层访问,也可以编译成字节码在虚拟机上运行
- JavaScript:嵌入HTML的脚本语言
- Delphi:可视化开发工具,基于窗体和面向对象的方法
- Visul Basic .NET:面向对象,源代码编译为中间代码MSIL后通过 .NET Framework的CLR执行
5. 程序设计语言的分类
(1)命令式和结构化(C、PASCAL)
- 命令式:基于动作,把计算过程当做动作的序列
- 结构化
- 自顶向下逐步精化
- 模块组织
- 程序只包含顺序、分支、循环,且每种构造只允许单入口和单出口
特点:结构清晰,模块性强、可读性强
(2)面向对象(JAVA、C++、Smlltalk)
支持数据隐藏、数据抽象、继承、多态、用户自定义类型等新功能,不断抽象
(3)函数式(LISP、Haskell、Scala)
把函数当做函数
F1(n)=…
F2(n)=F1(F1(n))
F3(n)=F3(n-1)…
- 输入:函数的自变量
- 输出:将输入通过递归或嵌套其他函数组合起来
特点:表达式中的函数都可以用其他函数代替
(4)逻辑型(PROLOG)
以形式逻辑为基础,是一系列事实,数据对象,具体关系和规则的集合
断言 P:-P1,P2…PN(若P1,P2…为真,则P为真)
事实 P.
- 通过查询操作把事实和规则输入数据库
- 用户通过查询,执行程序,进行模式匹配
特点:适用于编写定理证明、专家系统、自然语言理解问题
6. 程序设计语言的成分
(1)数据成分
指出程序的数据类型——代表数据对象,完成值的布局,检查运算的应用是否正确
数据的定义
数据是程序操作的对象,在使用时需要分配内存空间
- 数据的名称:用户通过标识符命名
- 数据的类型:数据占用内存的大小和存放的方式
- 数据的存储类别:数据在内存的位置和生存期
- 数据的作用域:使用数据的代码范围
数据的分类
- 数据的值
- 常量:只具有右值(内容),且值不变
- 变量 :具有左值(存储单元)和右值(内容),且值可以改变
- 数据的作用范围
- 全局量:整个文件或程序。存储空间不会改变
- 局部量:一个函数或语句块。存储单元动态改变
- 数据的组织形式
- 基本类型
- 用户自定义类型
- 构造类型
- 其他类型
(2)运算成分
指出程序允许使用的运算符号和运算规则
- 算术运算
- 关系运算
- 逻辑运算
(3)控制成分
指出程序的控制结构
- 顺序结构:表示一个计算操作序列
- 选择结构:在多个分支中选择一个
- 循环结构:初始化,根据条件循环
(4)传输成分
指出程序允许的数据传输方式
- 赋值处理
- 数据输入
- 数据输出
(5)函数
结构
- 函数首部
- 返回值的数据类型
- 函数名
- 参数及其类型
- 函数体
使用
函数需要先声明,再引用。
- 值调用:将实参的值传递给形参
- 引用调用:当形参为引用时,对形参的修改即为对实参的修改
二:语言处理程序
1. 汇编程序
(1)汇编语言的源程序
由若干条指令语句组成
- 指令语句:也叫机器指令语句,汇编后产生机器代码(可以被CPU直接识别),在程序运行时完成指定的操作
- 传送指令
- 算术运算指令
- 逻辑运算指令
- 移位指令
- 转移指令
- 处理机控制指令
- 伪指令语句:汇编后不会产生机器代码,需要在汇编的时候就完成指定的工作
- 宏指令语句:将多次使用的程序定义为宏,每个宏都有宏名,可以在任意位置引用。
(2)汇编程序的过程
将每一条可执行汇编语句转换成对应的机器指令,处理伪指令,一般需要两次扫描。
第一次扫描——记录符号和符号值
- 设立符号表ST
- 使用机器指令表MOT1,查找机器指令的记忆码和指令长度
- 设立一个位置(单元地址)计数器LC,每处理完一条机器指令或源指令,LC增加相应的长度,表示下一条被汇编的指令的偏移地址
- 设立伪指令表POT1,记录伪指令助记符和相应的子程序入口
- LC初始化为0
- 打开源程序文件,读入语句
- 当前语句有标号:将标号和LC的值记录在ST中
- 当前语句是可执行的汇编指令语句:查找MOT1,获取指令长度K,LC=LC+K
- 当前语句是伪指令:查找POT1,调用子程序——在汇编时处理
- 当前语句的操作码是非法记忆码:调用出错处理子程序
- 继续读下一条,直到END语句
- 关闭源程序文件
第二次扫描——产生目标程序
- 第一次扫描的符号表ST
- 使用机器指令表MOT2,查找机器指令助记符和机器指令二进制操作码、格式、长度
- 设立伪指令表POT2,记录伪指令记忆码和相应的子程序入口
- 打开源程序文件,读入语句
- 当前语句是可执行的汇编指令语句:查找MOT2,把机器指令助记符转换为二进制机器指令操作码
- 求出操作数区各个操作数的值,用二进制表示。如果出现符号,则通过查找ST获取其对应的指令地址
- 当前语句是伪指令:查找POT2,调用子程序
- 当前语句的操作码是非法记忆码:调用出错处理子程序
- 继续读下一条,直到END语句
- 关闭源程序文件
2. 编译程序
前端——与机器无关
(1)词法分析
基本概念
1:字符串
- 字母表∑:字母表是字符的非空有穷集合
- 字符串:字母表∑中的字符组成的有穷序列
- 长度:字符的个数
- 方幂:将自身连接N次 A0=ε
- 空串ε:由0个字符组成的序列
- 连接:字符串S和T的连接,记为S * T,S * ε=ε * S=S
- 字符表的所有字符串∑ *(包含空串)
- 集合运算:
- 或(A∪B):字符串集合A或B的字符串X
- 积(AB):字符串集合A和B共同字符串X
- 幂(An):字符串集合的自连接N次,A0=ε
- 正则闭包(A+):A1∪A:2∪…∪An∪…
- 闭包(A*):A0∪A+
2:文法
描述语言语法结构的规则,用四元组表示:G=(VN,VT,P,S)
- 词汇表V:V中的元素为文法的符号
- 非终结符集VN:非空有限集,每个元素为非终结符
- 终结符集VT:非空有限集,每个元素为终结符
- 产生式集合P:有限集,每个产生式为一个规则。如A→B(A∈V+,B∈V*),A包含至少一个非终结符。A→B1,A→B2 => A→B1|B2(Bi为A的候选式)
- 开始符号S:至少要在一条产生式中作为左部出现
分类:
- 0型文法
- 短语文法,递归可枚举
- 1型文法:G的任意产生式A→B,满足A的长度≤B的长度
- 上下文有关文法 ,替换非终结符需要考虑上下文
- 2型文法:G的任意产生式A→B,满足A∈VN
- 上下文无关文法:替换非终结符不需要考虑上下文
- 3型文法:G的任意产生式A→a或A→aB,满足a∈VT,A和B∈VN
- 正规文法/线性文法
3:推导和规约
推导
推导:从文法G的开始符号S出发,反复使用产生式,替换左部的非终结符为右部的文法符号序列,直到产生一个终结符的序列
直接推导:对于A→B,存在xAy => xBy(x,y∈V*)
- 对于P的每个产生式都有A => B
- 若文法中存在推导序列A0=>A1=>…=>AN,则A0可以推导出AN,表示为A0=+=>AN,(A0=*=>AN表示A0=+=>AN和A0=A0)
规约
规约:若文法G存在x =*=> y,则y可规约为x,x是y的一个规约
直接规约:若文法存在直接推导A => B,则B可直接规约为A,A是B的一个直接规约
4:句型、句子、语言
- 句型:从开始符号S推导出的符号串为文法的一个句型
- 句子:仅含终结符的句型是一个句子
- 语言:有限字母表∑上的有限字符串的集合,即从S出发能推导出的全体句子,记为L(G)(若L(G1)=L(G2),则G1和G2等价)
词法分析
对源程序逐个字符扫描,识别“单词”符号,把字符串转换为单词符号序列,一般将“单词”以二元组的方式输出:(单词种别,单词自身的值)。词法规则使用3型文法或正规表达式表示,
- 关键字:保留
- 运算符:保留
- 分隔符:保留
- 标识符:(用户标识符id,单词值)
- 常数:(数据类型,常数值)
1:正规式和正规集
正规式的概念
字母表∑中的每个字符通过或|、连接、闭包*进行连接而成的表达式为正规式
正规集的概念
若∑={A,B}
-
L={A}:存在正规式A,或者A是∑上的字符
- A:字符串A构成的集合 A*:多个A构成的集合
- B:字符串B构成的集合 B*:多个B构成的集合
- AB:字符串AB构成的集合 (AB)*:多个AB构成的集合
- A|B:字符串A或B构成的集合 (A|B)*:多个A或B构成的集合
- A(AB)*:以A开头的AB的集合 (AB)*A:以A结尾的AB的集合
- A(A|B)*:以A开头的A或B的集合 (A|B)*A:以A结尾的A或B的集合
-
(L(A))*:正规式A的闭包A*也是正规式,则正规集的闭包也是正规集
-
L(A)∪L(B):正规集的并集也是正规集
-
L(A)*L(B):正规集的交集也是正规集
正规式的性质
- 等价:若两个正规式的正规集相同,则两个正规式等价 U=V
- 交换律:U|V=V|U
- 结合律:U|V |W=U| V|W,UV W=U VW
- 分配律:(U|V)W=UW|VW
- 闭包:V*=V+ | ε,V**=V*
2:有限自动机——准确识别正规集
确定的有限自动机DFA
组成:
- 有限集合S
- 有穷字母表∑
- 单值映像F,F(A,X)=B表示当前状态为A,输入X时转换到下一状态B,B为A的后继状态
- S0:唯一开始状态
- Z:非空的终止状态集合
表示:
- 状态转换图:每个状态对应一个结点,双圈表示终止结点,转换过程对应有向弧。
- 若存在一条路径从起点指向终点,且转换过程的字符串s属于字母表,则s可由该DFA识别
-若起点和终点相同,则空字符串可被DFA识别
- 若存在一条路径从起点指向终点,且转换过程的字符串s属于字母表,则s可由该DFA识别
- 状态转换矩阵:行表示状态,列表示转换过程
不确定的有限自动机NFA
特点:
- 对于给定的状态和和转换字符,只能确定一个字符集合
- 转换字符可以是空串
- DFA是NFA的特例,对于任意一个NFA,都存在一个DFA
NFA→DFA
若A是NFA的状态集合的子集,则A的ε_闭包(A)就是从A的状态出发,经过ε到达的状态集合
若a是∑的一个字符,则Aa=ε_闭包(J)(其中J表示从A的状态出发,经过a到达的状态集合)
子集法:若NFA=(S,∑,F,S0,Z),DFA=(s,∑,f,s0,z)
- 求出DFA的初始状态s0,:根据NFA的初始状态,s0=ε_闭包(S0)
- 此时s中仅含有未标记的s0,对于s中未标记的状态集si
- 标记si,表示开始处理
- 对于每个字母表∑的字符a,求出si通过字符a到达的状态集合T=F(si,a)
- 求出si的闭包——sj=ε_闭包(T)
- 若sj是终点,则作为新的状态并且是终点加入到s,f(si,a)=sj,进行第6步
- 若sj不在s中,则将其作为未标记的新状态加入到s,f(si,a)=sj
- 重复进行,直到s中全部状态已标记
- 此时求出s,则z={q|q∈s,且q∩z=∅}
DFA最小化
可区分字符串:
- 若从状态A出发接受输入的字符串s,而另一个状态B不接受s
- 从状态A和B出发到达不同的接受状态
对于不可区分的状态(两个状态可以通过同一个字符串X到达同一个终点),可以合并为等价状态(去掉其中一个状态)
NFA→正规式
- 在NFA的状态转换图上加入两个结点。结点A到起点s0引一条弧并用ε标记,从所有终点到结点B引一条弧并用ε标记,此时图上只有1个起点A和1个终点B
- 除去除了AB之外的所有结点,用正规式代替弧
- 连接:1→x→2→y→3 ——> 1→xy→3
- 或:1→x→2,1→y→2 ——> 1→x|y→2
- 闭包:1→x→2→y→3,2→z→2 ——> 1→xz*y→3
正规式→NFA
- 对于正规式R,扩展状态转换图,加上起点A和终点B,弧用R标记
- 通过对R的分裂生成新结点,将弧的标记为字符或ε
- 连接:1→xy→3 ——> 1→x→2→y→3
- 或: 1→x|y→2 ——> 1→x→2,1→y→2
- 闭包: 1→xz*y→3 ——> 1→x→2→y→3,2→z→2
3:构造词法分析器
- 用正规式描述语言的单词构成规则
- 为正规式构造NFA,识别正规式表示的正规集
- 将NFA转换为DFA
- 最小化DFA
- 通过DFA构造词法分析器
(2)语法分析
根据语法规则,将单词符号序列分解为各类语法单位,如果源程序没有语法错误,则可以正确构造出语法树
- 表达式
- 语句
- 程序
1:上下文无关文法的概念
文法G的任意产生式A→B,满足A∈VN
规范推导(最右推导):如果在推导的任何一步A => B都是对A的最右或最左的非终结符进行替换,则推导为最右或最左推导
短语:若AxB、AyB是文法G的句型,并且y可以推导出x,则x是句型AxB相对于非终结符y的短语
- 如果y可以直接推导出x,则x是直接短语。一个句型最左直接短语为该句型的句柄
2:自顶向下的语法分析
对于给定的字符串X,从一个文法的开始符号S出发,进行最左推导并建立语法树,直到得到一个合法的句子或发现一个非法结构。
解决的问题:
- 递归问题:若文法中存在A→Aa,可能会陷入死循环,需要消除左递归
- 消除直接左递归:改变非终结符Vn和产生式P,成为新的文法
- 若A→Aa|b,则A→bA’、A‘→aA’|ε
- 若A→Aa1a2…|b1b2…,则A→b1A’|b2A’…、A‘→a1A’|a2A’|…ε
- 消除全部左递归:先将产生式的右部进行替换,把非直接左递归转换为直接左递归,再进行消除
- 消除直接左递归:改变非终结符Vn和产生式P,成为新的文法
- 回溯问题:若文法中存在A→ab|ac,即产生式有多个候选项的前缀相同,会导致回溯,需要提取左因子
- A→ab1|ab2|…,则A→aA’、A’→b1|b2…
- 反复提取,直到每个非终结符的任意两个候选式不含有公共前缀
LL(1)文法
对于文法G的任意两个候选式A→a|b满足条件:
- 对于任意终结符a,a和b不能同时推导出以a开始的文法序列
- a和b最多只有一个可以推导出ε
- 若b是ε的一个规约,则a不能推导出以FOLLOW(A)终结符开始的任何文法符号序列
文法符号序列a的FIRST集合:从a出发可以推导出所有以终结符号开头的序列,其中所有开头终结符号构成的集合。FIRST(a)={x|a=*=>x,x∈Vt}
- 若文法符号X是终结符,则X加入集合
- 否则看它的产生式
- 若X→ε,则ε加入集合
- 若X→Y,并且Y是终结符,则Y加入集合
- 若X→Y1Y2…,并且Y1是非终结符,则把FIRST(Y1)加入集合。
若ε是Y1Y2…的一个规约,则把FIRST(Yi)加入集合
A的FOLLOW集合:从开始符号出发推导出的所有含A的句型中,紧跟在A之后的终结符构成的集合。FOLLOW(A)={a|S=*=>Aa,a∈Vt}
- 若W文法符号A是开始符号,则#加入集合FOLLOW(A)
- 若B→aAb,则FIRST(b)除了ε加入集合FOLLOW(A)
- 若B→aA,则FOLLOW(B)加入FOLLOW(A)
- 若B→aAb,且ε∈FIRST(b),则FOLLOW(B)加入FOLLOW(A)
递归下降分析法
对于LL(1)文法,为每个非终结符构造子程序,子程序按照产生式的候选项分情况展开,遇到终结符则进行匹配,遇到非终结符则调用相应的子程序
- 简单,易构造
- 程序与文法相关,对文法的改变需要对程序进行修改
预测分析法
使用一张预测分析表,表的横坐标为非终结符,纵坐标为终结符,表的元素M[A,a]为关于A的产生式,表示当遇到符号a时用A推导应采用的产生式
构造表:
- 对于每个文法产生式A→a|b,拆为两个部分A→a,A→b。分别观察
- 将FIRST(a)的每个终结符a,加入A→a到M[A,a]。
- 若FIRST(a)包含ε,则对FOLLOW(A)的每个终结符b,加入A→a到M[A,b]
- 其他空格置为error
预测过程:
- 将#和文法开始符号s压入栈,栈顶符号记为TOP,输入符号记为X
- TOP=#,X=#,分析成功
- TOP=#,X≠#,分析错误
- TOP为终结符,TOP=X,则出栈,读入下一个符号
- TOP为终结符,TOP≠X,出错
- TOP为非终结符,M[TOP,X]=产生式A→a,则把a的符号从右到左入栈
- TOP为非终结符,M[TOP,X]=error,出错
3:自底向上的语法分析(移近-规约)
对于给定的字符串X,自左向右扫描,将输入符号逐个入栈,如果栈顶符号形成某个句型的可规约串,则用产生式的左部非终结符来代替,直到栈中只剩下开始符号则成功
LR分析法
根据当前分析栈中的符号串和向右顺序查看输入串的第N个符号,唯一缺点分析器的工作是移进还是规约,也就能唯一确定句柄
LR分析器的组成
- 驱动器
- 分析表
- 动作表
- 状态转换表
- 分析栈
- 符号栈
- 状态栈
(3)语义分析
分析语法结构的含义,检查源程序是否包含静态语义错误,收集类型信息。
- 进行类型分析和检查:检查数据类型的载体和运算是否正确
- 内部表示源程序
- 声明语句:使用符号表记录符号信息如符号,符号类型,符号的逻辑地址
- 可执行语句:检查结构合理的表达式是否有意义
形式化方法
- 属性文法
- 公理语义
- 操作语义
- 指标语义
(4)中间代码生成
根据语义分析输出中间代码
语法制导翻译
- 将语言结构的语义以属性的形式赋予该结构的文法符号
- 将属性的计算以语义规则的形式赋予文法的产生式
- 通过执行语义规则,实现对属性的计算,处理语义
中间代码的形式
- 后缀式:将运算符写在运算对象后面(不需要使用括号,方便用栈求值)
- 树形表示:运算符号为父结点,运算对象为两个子节点
- 三元表示:(运算符,运算对象1,运算对象2)
- 四元表示:(运算符,运算对象1,运算对象2,运算结果)
语法结构的翻译
使用到的语义变量:
语义变量 | 语义过程 |
---|---|
Entry(id) | 在符号表查找id并获取入口 |
S.code | 翻译S形成的代码序列 |
E.place | 存放非终结符E的变量名,在符号表的入口或整数码 |
E.tc | E为真 |
E.fc | E为假 |
GEN(OP,ARG1,ARG2,RESULT) | 产生一个四元式 |
NXQ | 下一个四元式地址的编号 |
Merg(P1,P2) | 合并P1,P2两条链 |
Backpatch(p,t) | 把p连接的所有四元式的第四项结果作为t(回填) |
Newtemp | 生成新的临时存储单元 |
拉链:四元式的转向地址不确定,将所有转向同一地址的四元式链接成表
回填:四元式的转向地址确定后,沿着链表填入地址
赋值语句
操作 | 文法 | 语义规则 |
---|---|---|
保存值 | A→id:=E | {GEN(:=,E.place,-,Entry(id)) } |
加法 | E→E1+E2 | {E.place:=Newtemp;GEN(+,E1.place,E2.place,E,place) } |
乘法 | E→E1*E2 | {E.place:=Newtemp;GEN( * ,E1.place,E2.place,E,place) } |
取反 | E→-E1 | {E.place:=Newtemp;GEN( @ ,E1.place,-,E,place) } |
圆括号 | E→(E1) | {E.place:=E1.place } |
赋值给变量 | E→id | {E.place:=Entry(id)} |
判断语句
操作 | 文法 | 语义规则 | 说明 |
---|---|---|---|
与 | E→ E1 and E2 | 进行拆分 | |
E∩ → E1 and | {Backpath(E1.tc,NXQ) ; E∩.fc=E1.fc} | 如果E1为假,直接传假的出口 否则,判断E2,回传真的出口 |
|
E→E∩ E2 | {E.tc:=E2.tc ; E.fc=Merg(E∩.fc,E2.fc)} | 如果E2也为真,传真的出口 合并假的出口 |
|
或 | E→E1 or E2 | 进行拆分 | |
E∪→E1 or | {Backpath(E1.fc,NXQ) ; E∪.tc=E1.tc} | 如果E1为真,直接传真的出口 否则,判断E2,回传假的出口 |
|
E→E∪ E2 | {E.fc:=E2.fc ; E.tc=Merg(E∪.tc,E2.tc)} | 如果E2也为假,传假的出口 合并真的出口 |
|
非 | E→not E1 | {E.tc:=E1.fc ; E.fc=E1.tc} | 交换真假出口 |
括号 | E→(E1) | {E.tc:=E1.tc ; E.fc=E1.fc} | 传真的和假的出口 |
赋值 | E→id | {E.tc:=NXQ ; E.fc:=NXQ+1 ; GEN(jnz,Entry(id),-,0) ; GEN(j ,- ,-,0)} | 等待回填 |
关系运算 | E→id1 relop id2 | {E.tc:=NXQ ; E.fc:=NXQ+1 ; GEN(j relop(运算符),Entry(id1),Entry(id2),0) ; GEN(j,-,-,0)} | 等待回填 |
条件语句
操作 | 文法 | 语义规则 | 说明 |
---|---|---|---|
赋值语句 | S→A | {S.chain=0} | 无出口链 |
if语句 | S→if E then S1 else S2 | 拆分 | |
C→if E then S1 | {Backpatch(E.tc,NXQ) ; C.chain:=E.fc} | 如果为真,需要回填真出口;否则传假出口 | |
S→CS1 | S.chain:=Merg(C.chain,S1.chain) | 拉链 | |
Tq→CS2 else | {q:NXQ ; GEN(J,-,-,0) ; Tq.chain:=Merg(S2.chain,q) ; Backpatch(C.chain,NXQ)} | 如果为真需要回填真出口 | |
S→TpS1 | {S.chain:=Merg(Tp.chain,S1.chain);} | 拉链 | |
while语句 | S→while E do S1 | 拆分 | |
W→while | {W:quad:=NXQ} | 记录首地址 | |
Wd→W E do | {Backpatch(E.tc,NXQ) ; Wd.chain:=E.fc} | 回填真出口,传假出口 | |
S→WdS1 | {Backpatch(S1.chain,Wd.quad); GEN(j,-,-,Wd.quad) ; S,chain = Wd.chain} | 用首地址回填真出口,跳转出口 |
过程(函数)调用
依赖于形参和实参的结合方式和数据空间的分配方式
- 静态分配:在编译时确定目标程序所需的全部数据空间的大小,并确定每个数据对象的存储位置(逻辑地址)
- 动态分配
- 栈分配(先申请后释放):将数据空间设计为栈,调用过程时入栈,过程结束后出栈
- 堆分配:将数据空间设计为堆
活动:过程的一次调用称为活动
活动记录:活动的运行环境
- 参数、返回地址
- 机器状态
- 局部数据、临时变量
- 控制链、访问链
call P(T1,T2…) 产生四元式序列 para T1,para T2…call P,n
后端——与机器有关
(5)代码优化
在中间代码生成或目标代码生成时,分析程序的控制流和数据流,对程序进行等价变换,使得程序生成更有效的目标代码
- 中间代码的形式
- 解释器:适用于树型和后缀型
- 编译器:适用于四元式
- 目标代码的形式
- 汇编语言:便于分析调试
- 机器指令
- 绝对机器指令:便于立即执行
- 可再定位机器指令 :便于保存在任意位置
- 寄存器的分配
- 计算的次序
(6)目标代码生成
把中间代码变换为特定机器的绝对指令代码、可重定位的指令代码、汇编指令代码
符号表的管理
记录源程序各个符号的必要信息,可以在词语分析、语法分析、语义分析开始建立
出错处理
- 静态错误:编译阶段出现的错误
- 语法错误:拼写错误,符号错误
- 静态语义错误 :语义分析阶段的类型不匹配错误
- 动态错误:程序运行阶段的错误
3. 解释程序
不会产生目标程序,直接执行源程序或源程序的中间表示形式
(1)执行方式
直接执行源程序
对源程序进行逐个字符的检查,然后执行程序语句规定的动作
- 效率低
执行中间表示形式
把源程序翻译某种中间代码的形式,再解释中间代码
- 可移植
(2)解释程序的结构
分析部分——翻译为中间代码
- 词法分析
- 语法分析
- 语义分析程序
解释部分——对第一部分进行解释
- 程序计数器PC:记录执行的中间代码的位置
- 内存数组M:存放中间代码和解释部分的子程序
- PC:=PC+1
- 执行位于opcode-table[M[PC]]的子程序