当前位置:文档之家› 第6章 语义分析(1)

第6章 语义分析(1)

层数为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)
相关主题