第6章 语义分析(1)
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
层数为1 x: (1,0);
类型的内部表示
为什么需要类型的内部表示?
– 类型是标识符的重要属性; – 类型检查是语义分析的重要部分; – 类型的结构对类型检查很重要;
程序语言的类型
– 基本类型 Integer (int ) Real (float) Bool Char – 构造类型 数组(Array) 结构体(Structure、record) 联合类型(Union、变体记录) – 枚举类型 – 指针类型
联合类型的内部表示
Kind Body unionT y Body: 指向联合体中域定义链表 Size: 所有域的类型的size中最大值;
Body
FieldName FieldType Next
Size
typedef union{ int i; char name[10]; real x; } test;
基本类型的内部表示
Size intPtr Kind
intSize
boolSize realSize
intTy
boolTy realTy
boolPtr realPtr
charPtr
charSize
charTy
数组类型的内部表示
Size
Kind arrayTy
Low
Up
ElemTy
size = sizeof(ElemTy) (Up-Low+1) ElemTy: 指向数组成分类型的指针; array [2..9] of integer; (8, arrayTy, 2, 9, intPtr)
第六章 语义分析
6.1 语义分析概述
6.2 符号表
6.3 类型的语义分析 6.4 声明的语义分析 6.5 程序体的语义分析 么是符号表(Symbol Table)?
– 符号表可看作是从标识符名字到它的属性的映射; – 用于存储程序中声明的标识符及其属性;
语义分析在编译程序中的作用
词法分析 目标代码生成
语法分析
中间代码优化
语义分析
中间代码生成
分析
合成
第六章 语义分析
6.1 语义分析概述
6.2 符号表
6.3 类型的语义分析 6.4 声明的语义分析 6.5 程序体的语义分析 6.6 属性文法和动作文法
6.1 语义分析概述
语法(Syntax)和语义(Semantics)的区别 语义分析的必要性 程序设计语言语义的分类 如何描述程序设计语言的语义? 语义分析的主要任务
Access: (dir, indir); Level: 层数 Offset:偏移量
域名标识符的属性
TypePtr
Kind fieldKind
Offset
Ptr
TypePtr
– 指向域名类型的内部表示;
Kind
– 标识符的种类, 所有域名标识符的Kind =fieldKind;
Offset:
P(0,-)
层数为2
i : (2, 0) j : (2,1) 层数为1 x : (1, 0)
f(1,-)
Procedure R() Var x:real; Begin …… End; Begin …… End.
R(0,-)
变量的层数和偏移 (包括形式参数)
typedef struct { int number; char name[10];} example; int i; example p; real x;
符号表的组织
标识符的作用域 符号表的局部化 符号表的全局化 接口函数
标识符的作用域
作用域(scope)
– 程序中的每个标识符都有自己的作用域;
– 标识符的作用域是标识符可见(visible)或有效的(effective)
形式语义技术没有形式语法成熟 硕士研究生的课程-《形式语义学》
语义分析的主要任务
根据声明部分建立符号表
– 存储标识符的属性,以便检查语义错误和为代
码生成提供信息;
在整个程序范围内检查语义错误
– 声明和使用相关的错误 – 类型相关的语义错误
一般地,语义分析是伴随着语法分析完成 的;
常见的语义错误
– 该域名针对所在结构类型的偏移量;
Ptr:
– 指向所在结构体的下一个域;
(实在过程或者函数的属性)
TypePtr Kind routKind
Class actual
Level
Param
Code
Size
Forward
TypePtr: 指向函数返回值类型的内部表示,过程情形为空; Kind: 标识符的种类,此处为 routKind; Class: actual指代实在的(有自己的函数体)函数或过程; Level: 标识符被定义的层数; Param: 指向形式参数列表的指针; Code: 指向函数或过程对应的目标代码的起始地址; Size: 目标代码的大小; Forward: 该函数为向前声明时取值 = true;
标识符名字 x p …
属性 int, variable, …… void, function, (int i), …… …….
6.2 符号表
为什么在语义分析时需要符号表(Symbol Table)?
– 从标识符的Token定义,我们仅仅知道了标识符的名字,
对于其它属性,例如类型,种类等没有记录
– 对于标识符的更多信息需要进行语义分析,从而检查
声明和使用相关的语义错误
– 标识符没有声明; – 重复声明;
如何检查?
– 每当遇到新声明的标识符,查符号表
如果当前有效的所有标识符中有相同名字的,则是重复声 明错误; 否则生成它的属性信息,保存到符号表中; 如果没有找到,说明该标识符没有声明; 否则, 得到该标识符的属性,进行进一步分析;
– 每当遇到标识符的使用,查符号表
常量标识符的属性
TypePtr Kind
constKind
Value
TypePtr
– 指向常量类型内部表示的指针;
Kind
– 标识符的种类,所有常量的 kind = constKind;
Value
– 常量的值;
类型标识符的属性
TypePtr Kind
typeKind
TypePtr
– 指向类型内部表示的指针;
Kind
– 标识符的种类,所有类型标识符的 kind = typeKind;
变量标识符的属性
TypePtr Kind varKind
Access
Level
Offset
TypePtr
– 指向变量类型的内部表示;
Kind
– 标识符的种类, 所有变量标识符的Kind = varKind;
语义分析的必要性
一个语法正确的程序不能保证它是有意义 的! 程序中容易出现各种语义错误:
– 标识符未声明 – 操作数的类型与操作符的类型不匹配
– ……
程序设计语言语义的分类
静态语义
– 编译时(compile-time)可以检查的语义 – 例如:标识符未声明
动态语义
– 目标程序运行时(run-time)才能检查的语义
标识符的内部表示
不同种类标识符的属性
– 常量: (类型,值) – 类型: (类型) – 变量: (类型, 层数,偏移) – 函数: (返回类型, 形参定义, 代码地址, 空间大
小, …….) – 过程: (形参定义, 代码地址, 空间大小, …….) – 域名: (类型, 偏移, 所在结构类型)
形参过程或者函数的属性
TypePtr Kind Class Level Param Offset
routKind formal
TypePtr:同形式函数/过程; Kind: 同形式函数/过程, = routKind; Class: = formal; Level:同形式函数/过程; Param:同形式函数/过程; Offset: 在形参列表中的偏移;
语义错误;
标识符名字 x p …
属性 int, variable, …… void, function, (int i), …… …….
6.2 符号表
为表示标识符的属性,我们需要建立
– 标识符的内部表示
– 类型的内部表示 – 值的内部表示
符号表的组织
– 标识符的作用域 – 局部化的符号表
– 全局化的符号表
Program A Var x, y: integer; Procedure P() Var k:array[1..9] of real; m: integer;
Function f(i:integer) Var j:integer; Begin …… End; Begin …… End; 层数为0 层数为1 k : (1, 0) m : (1,18) x : (0, 0) y : (0, 1)
语法和语义的区别
语法:
– 是描述一个合法定义的程序结构的规则 – 例如:<函数调用语句> id( <实参表达式>)
语义:
– 说明一个合法定义的程序的含义 int x; 符合变量声明的语法、语义
float f();
x(); x = f(); 符合函数声明的语法、语义 符合函数调用的语法、不符合语义 符合赋值语句的语法、不符合语义
变量的层数和偏移 (包括形式参数)
层数(level)
– 某些程序语言,过程/函数的定义可以嵌套
– 通常规定主程序的层数为0;
偏移量(offset)
– 执行过程/函数的调用时, 需要为其中的变量分配
空间; – 偏移量指的是变量针对其所在过程/函数的空间 的首地址的偏移量;
变量的层数和偏移 (包括形式参数)
指针类型的内部表示
Size
Kind ptrTy
BaseType
BaseType: 指向指针的基类型; Size:指针的空间大小(通常为一个单元);
枚举类型的内部表示
Kind EList enumT y EList: 枚举常量链表; Size:枚举类型值的空间大小(通常为一个单元); Size
– 符号表的接口函数
知识关系图
语义分析
需要标识符的更 多信息
符号表
标识符的内部表示
标识符的属性
类型的内部表示
值的内部表示
标识符的内部表示
标识符的不同种类
– 常量标识符 – 类型标识符 – 变量标识符 – 函数标识符
– 过程标识符
– 域名标识符
typedef enum {constKind, typeKind, varKind, procKind, funcKind, fieldKind} idKid;
int [7]; (7, arrayTy, 0, 6, intPtr)
结构体类型的内部表示
Size
Kind strutTy
Body
Body: 指向结构体中域定义链表 Size: 所有域的类型的size的总和;
Body
FieldName FieldType Offset Next
typedef struct { int i; char name[10]; real x; }example;
常见的语义错误
类型相关的语义错误
– 各种条件表达式的类型不是布尔类型; – 运算符的分量类型不相容;
– 赋值语句左右类型不相容;
– 形实参类型不相容; – 函数调用参数个数不同;
– f(…..)中f不是函数标识符;
– 下标表达式(数组)不是合法类型; – 函数说明和函数返回类型不相容;
– ……
值的内部表示
可表示的值
– – – – –
Integer Real False, true(通常对应0和1) char: ASCII码值 枚举类型: 对应整数 数组 结构 联合 指针
结构值
– – – –
知识关系图
语义分析
需要标识符的更 多信息
符号表
标识符的内部表示
标识符的属性
类型的内部表示
值的内部表示
– 例如:除零溢出错误
如何描述程序设计语言的语义?
程序设计语言的形式语义
– 属性文法 (用于描述静态语义)
– 操作语义(Operational Semantics) – 指称语义(Denotational Semantics)
– 代数语义(Algebra Semantics)
– 公理语义(Axiomatic Semantics)
void q(int i) { real x, y; …… } void main() { int x; …… } 层数为0 i : (0, 0) p: (0, 1) x : (0, 12) q: (0,…) main: (0,…) 层数为1
i: (1, 0) x : (1, 1) y : (1,3)
类型的内部表示
为什么需要类型的内部表示?
– 类型是标识符的重要属性; – 类型检查是语义分析的重要部分; – 类型的结构对类型检查很重要;
程序语言的类型
– 基本类型 Integer (int ) Real (float) Bool Char – 构造类型 数组(Array) 结构体(Structure、record) 联合类型(Union、变体记录) – 枚举类型 – 指针类型
联合类型的内部表示
Kind Body unionT y Body: 指向联合体中域定义链表 Size: 所有域的类型的size中最大值;
Body
FieldName FieldType Next
Size
typedef union{ int i; char name[10]; real x; } test;
基本类型的内部表示
Size intPtr Kind
intSize
boolSize realSize
intTy
boolTy realTy
boolPtr realPtr
charPtr
charSize
charTy
数组类型的内部表示
Size
Kind arrayTy
Low
Up
ElemTy
size = sizeof(ElemTy) (Up-Low+1) ElemTy: 指向数组成分类型的指针; array [2..9] of integer; (8, arrayTy, 2, 9, intPtr)
第六章 语义分析
6.1 语义分析概述
6.2 符号表
6.3 类型的语义分析 6.4 声明的语义分析 6.5 程序体的语义分析 么是符号表(Symbol Table)?
– 符号表可看作是从标识符名字到它的属性的映射; – 用于存储程序中声明的标识符及其属性;
语义分析在编译程序中的作用
词法分析 目标代码生成
语法分析
中间代码优化
语义分析
中间代码生成
分析
合成
第六章 语义分析
6.1 语义分析概述
6.2 符号表
6.3 类型的语义分析 6.4 声明的语义分析 6.5 程序体的语义分析 6.6 属性文法和动作文法
6.1 语义分析概述
语法(Syntax)和语义(Semantics)的区别 语义分析的必要性 程序设计语言语义的分类 如何描述程序设计语言的语义? 语义分析的主要任务
Access: (dir, indir); Level: 层数 Offset:偏移量
域名标识符的属性
TypePtr
Kind fieldKind
Offset
Ptr
TypePtr
– 指向域名类型的内部表示;
Kind
– 标识符的种类, 所有域名标识符的Kind =fieldKind;
Offset:
P(0,-)
层数为2
i : (2, 0) j : (2,1) 层数为1 x : (1, 0)
f(1,-)
Procedure R() Var x:real; Begin …… End; Begin …… End.
R(0,-)
变量的层数和偏移 (包括形式参数)
typedef struct { int number; char name[10];} example; int i; example p; real x;
符号表的组织
标识符的作用域 符号表的局部化 符号表的全局化 接口函数
标识符的作用域
作用域(scope)
– 程序中的每个标识符都有自己的作用域;
– 标识符的作用域是标识符可见(visible)或有效的(effective)
形式语义技术没有形式语法成熟 硕士研究生的课程-《形式语义学》
语义分析的主要任务
根据声明部分建立符号表
– 存储标识符的属性,以便检查语义错误和为代
码生成提供信息;
在整个程序范围内检查语义错误
– 声明和使用相关的错误 – 类型相关的语义错误
一般地,语义分析是伴随着语法分析完成 的;
常见的语义错误
– 该域名针对所在结构类型的偏移量;
Ptr:
– 指向所在结构体的下一个域;
(实在过程或者函数的属性)
TypePtr Kind routKind
Class actual
Level
Param
Code
Size
Forward
TypePtr: 指向函数返回值类型的内部表示,过程情形为空; Kind: 标识符的种类,此处为 routKind; Class: actual指代实在的(有自己的函数体)函数或过程; Level: 标识符被定义的层数; Param: 指向形式参数列表的指针; Code: 指向函数或过程对应的目标代码的起始地址; Size: 目标代码的大小; Forward: 该函数为向前声明时取值 = true;
标识符名字 x p …
属性 int, variable, …… void, function, (int i), …… …….
6.2 符号表
为什么在语义分析时需要符号表(Symbol Table)?
– 从标识符的Token定义,我们仅仅知道了标识符的名字,
对于其它属性,例如类型,种类等没有记录
– 对于标识符的更多信息需要进行语义分析,从而检查
声明和使用相关的语义错误
– 标识符没有声明; – 重复声明;
如何检查?
– 每当遇到新声明的标识符,查符号表
如果当前有效的所有标识符中有相同名字的,则是重复声 明错误; 否则生成它的属性信息,保存到符号表中; 如果没有找到,说明该标识符没有声明; 否则, 得到该标识符的属性,进行进一步分析;
– 每当遇到标识符的使用,查符号表
常量标识符的属性
TypePtr Kind
constKind
Value
TypePtr
– 指向常量类型内部表示的指针;
Kind
– 标识符的种类,所有常量的 kind = constKind;
Value
– 常量的值;
类型标识符的属性
TypePtr Kind
typeKind
TypePtr
– 指向类型内部表示的指针;
Kind
– 标识符的种类,所有类型标识符的 kind = typeKind;
变量标识符的属性
TypePtr Kind varKind
Access
Level
Offset
TypePtr
– 指向变量类型的内部表示;
Kind
– 标识符的种类, 所有变量标识符的Kind = varKind;
语义分析的必要性
一个语法正确的程序不能保证它是有意义 的! 程序中容易出现各种语义错误:
– 标识符未声明 – 操作数的类型与操作符的类型不匹配
– ……
程序设计语言语义的分类
静态语义
– 编译时(compile-time)可以检查的语义 – 例如:标识符未声明
动态语义
– 目标程序运行时(run-time)才能检查的语义
标识符的内部表示
不同种类标识符的属性
– 常量: (类型,值) – 类型: (类型) – 变量: (类型, 层数,偏移) – 函数: (返回类型, 形参定义, 代码地址, 空间大
小, …….) – 过程: (形参定义, 代码地址, 空间大小, …….) – 域名: (类型, 偏移, 所在结构类型)
形参过程或者函数的属性
TypePtr Kind Class Level Param Offset
routKind formal
TypePtr:同形式函数/过程; Kind: 同形式函数/过程, = routKind; Class: = formal; Level:同形式函数/过程; Param:同形式函数/过程; Offset: 在形参列表中的偏移;
语义错误;
标识符名字 x p …
属性 int, variable, …… void, function, (int i), …… …….
6.2 符号表
为表示标识符的属性,我们需要建立
– 标识符的内部表示
– 类型的内部表示 – 值的内部表示
符号表的组织
– 标识符的作用域 – 局部化的符号表
– 全局化的符号表
Program A Var x, y: integer; Procedure P() Var k:array[1..9] of real; m: integer;
Function f(i:integer) Var j:integer; Begin …… End; Begin …… End; 层数为0 层数为1 k : (1, 0) m : (1,18) x : (0, 0) y : (0, 1)
语法和语义的区别
语法:
– 是描述一个合法定义的程序结构的规则 – 例如:<函数调用语句> id( <实参表达式>)
语义:
– 说明一个合法定义的程序的含义 int x; 符合变量声明的语法、语义
float f();
x(); x = f(); 符合函数声明的语法、语义 符合函数调用的语法、不符合语义 符合赋值语句的语法、不符合语义
变量的层数和偏移 (包括形式参数)
层数(level)
– 某些程序语言,过程/函数的定义可以嵌套
– 通常规定主程序的层数为0;
偏移量(offset)
– 执行过程/函数的调用时, 需要为其中的变量分配
空间; – 偏移量指的是变量针对其所在过程/函数的空间 的首地址的偏移量;
变量的层数和偏移 (包括形式参数)
指针类型的内部表示
Size
Kind ptrTy
BaseType
BaseType: 指向指针的基类型; Size:指针的空间大小(通常为一个单元);
枚举类型的内部表示
Kind EList enumT y EList: 枚举常量链表; Size:枚举类型值的空间大小(通常为一个单元); Size
– 符号表的接口函数
知识关系图
语义分析
需要标识符的更 多信息
符号表
标识符的内部表示
标识符的属性
类型的内部表示
值的内部表示
标识符的内部表示
标识符的不同种类
– 常量标识符 – 类型标识符 – 变量标识符 – 函数标识符
– 过程标识符
– 域名标识符
typedef enum {constKind, typeKind, varKind, procKind, funcKind, fieldKind} idKid;
int [7]; (7, arrayTy, 0, 6, intPtr)
结构体类型的内部表示
Size
Kind strutTy
Body
Body: 指向结构体中域定义链表 Size: 所有域的类型的size的总和;
Body
FieldName FieldType Offset Next
typedef struct { int i; char name[10]; real x; }example;
常见的语义错误
类型相关的语义错误
– 各种条件表达式的类型不是布尔类型; – 运算符的分量类型不相容;
– 赋值语句左右类型不相容;
– 形实参类型不相容; – 函数调用参数个数不同;
– f(…..)中f不是函数标识符;
– 下标表达式(数组)不是合法类型; – 函数说明和函数返回类型不相容;
– ……
值的内部表示
可表示的值
– – – – –
Integer Real False, true(通常对应0和1) char: ASCII码值 枚举类型: 对应整数 数组 结构 联合 指针
结构值
– – – –
知识关系图
语义分析
需要标识符的更 多信息
符号表
标识符的内部表示
标识符的属性
类型的内部表示
值的内部表示
– 例如:除零溢出错误
如何描述程序设计语言的语义?
程序设计语言的形式语义
– 属性文法 (用于描述静态语义)
– 操作语义(Operational Semantics) – 指称语义(Denotational Semantics)
– 代数语义(Algebra Semantics)
– 公理语义(Axiomatic Semantics)
void q(int i) { real x, y; …… } void main() { int x; …… } 层数为0 i : (0, 0) p: (0, 1) x : (0, 12) q: (0,…) main: (0,…) 层数为1
i: (1, 0) x : (1, 1) y : (1,3)