编译器开发系列--Ocelot语言4.类型定义的检查
这里主要介绍一下检查循环定义的结构体、联合体。是对成员中包含自己本身的结构体、联合体进行检查。所谓“成员中包含自己本身”,举例来说,就是指下面这样的定义。
struct point {
struct point p;
};
这里所说的“成员中包含自己本身”是指直接包含自己本身,通过指针来应用自己本身是没有问题的。例如刚才的例子,如果是下面这样的话就没有问题了。
struct point {
struct point *ptr;
};
刚才的例子中存在直接的循环定义,因此一眼就能看出来。还有如下所示的间接循环定义的情况,也需要注意。
struct point_x {
struct point_y y;
};
typedef struct point_x my_point_x;
struct point_y {
my_point_x x;
};
上述例子中还夹杂着使用typedef 定义的类型,因此调查起来更为繁琐。
检查“循环定义的类型”的方法。进行这样的类型检查需要将类型定义的整体当作图(graph)来思考。
将类型的定义抽象为图时,可以将类型作为节点,将该类型对其他类型的引用作为边。例如结构体的定义,将该结构体的类型作为节点,向成员的类型的节点连接一条边。使用typedef 的情况下,将新定义的类型作为节点,向原来的类型节点引一条边。
再来看一个例子。现在假设有如下所示的定义。
struct st {
struct point pnt;
long len;
};
typedef unsigned int uint;
struct point {
uint x;
uint y;
};
将上述定义转化为图,如图10.2 所示。

如果发生循环定义,那么在生成类型定义的图时,图中某处必定存在闭环。循环定义情况下的图如图10.3 所示。

可见图中存在闭环。检查是否存在循环定义,只需检查类型定义的图中是否存在闭环即可。
检测有向图中的闭环的算法
因为边存在方向性,所以类型定义的图属于有向图。要检测有向图中是否存在闭环,可以使用如下算法。
1. 选择任意一个节点(类型)并标注为“查找中”
2. 沿着边依次访问所有与该节点相邻的节点
3. 如果访问到的节点没有标注任何状态,则将该节点标注为“查找中”;如果标注了“查找结束”,则不做任何处理,返回之前的节点;如果已经标注为“查找中”,则说明存在闭环
4. 从当前的节点重复步骤2 和3,如果已经没有可访问的相邻节点,则将该节点标注为“查找结束”,并沿原路返回
5. 按照上述流程对所有节点进行处理,如果查找过程中没有遇到“查找中”状态的节点,就说明不存在闭环
上述算法中使用了“有向图的深度优先检索”来检测闭环。简单地说,该算法的概要就是“只要节点有未访问的相邻节点就试着访问,调查是否会回到原来的节点”。从算法执行过程中的某一时刻来看,就是在为从起始节点到某一节点的路径上的所有节点标注上“查找中”的状态。
具体算法如下:
protected void checkRecursiveDefinition(Type t, ErrorHandler h) {
_checkRecursiveDefinition(t, new HashMap<Type, Object>(), h);
}
static final protected Object checking = new Object();
static final protected Object checked = new Object();
/*结构体、联合体的循环定义检查
* 结构体、联合体、数组、typedef 所定义的类型以外的类型只有整数类型和指针,因此除
了上述4 个类型以外,其他情况下都不可能出现边。包含某类型的指针的情况下,因为不会产
生循环依赖,所以不会有问题。
算法说明中的“标注状态”的实现方式是“将Type 对象和它的状态作为一组保存在
Map 对象marks 中”,这是上述算法的重点。
*/
protected void _checkRecursiveDefinition(Type t,
Map<Type, Object> marks,
ErrorHandler h) {
/*
* 如果t 的状态为“查找中”,输出错误并return
*/
if (marks.get(t) == checking) {
h.error(((NamedType)t).location(),
"recursive type definition: " + t);
return;
}
/*
* t 的状态为“查找结束”
*/
else if (marks.get(t) == checked) {
return;
}
/*
* 访问的节点还没有被标注状态。
* 将t 标注为“查找中”,
* 访问所有和t 相邻的节点(调用_checkRecursiveDefinition),
* 将t 标注为“查找结束”。
*/
else {
marks.put(t, checking);
if (t instanceof CompositeType) {
CompositeType ct = (CompositeType)t;
for (Slot s : ct.members()) {
_checkRecursiveDefinition(s.type(), marks, h);
}
}
else if (t instanceof ArrayType) {
ArrayType at = (ArrayType)t;
_checkRecursiveDefinition(at.baseType(), marks, h);
}
else if (t instanceof UserType) {
UserType ut = (UserType)t;
_checkRecursiveDefinition(ut.realType(), marks, h);
}
marks.put(t, checked);
}
}
编译器开发系列--Ocelot语言4.类型定义的检查的更多相关文章
- 编译器开发系列--Ocelot语言3.类型名称的消解
"类型名称的消解"即类型的消解.类型名称由TypeRef 对象表示,类型由Type 对象表示.类型名称的消解就是将TypeRef 对象转换为Type 对象. TypeResolve ...
- 编译器开发系列--Ocelot语言1.抽象语法树
从今天开始研究开发自己的编程语言Ocelot,从<自制编译器>出发,然后再自己不断完善功能并优化. 编译器前端简单,就不深入研究了,直接用现成的一款工具叫JavaCC,它可以生成抽象语法树 ...
- 编译器开发系列--Ocelot语言6.静态类型检查
关于"静态类型检查",想必使用C 或Java 的各位应该非常熟悉了.在此过程中将检查表达式的类型,发现类型不正确的操作时就会报错.例如结构体之间无法用+ 进行加法运算,指针和数值之 ...
- 编译器开发系列--Ocelot语言2.变量引用的消解
"变量引用的消解"是指确定具体指向哪个变量.例如变量"i"可能是全局变量i,也可能是静态变量i,还可能是局部变量i.通过这个过程来消除这样的不确定性,确定所引用 ...
- 编译器开发系列--Ocelot语言7.中间代码
Ocelot的中间代码是仿照国外编译器相关图书Modern Compiler Implementation 中所使用的名为Tree 的中间代码设计的.顾名思义,Tree 是一种树形结构,其特征是简单, ...
- 编译器开发系列--Ocelot语言5.表达式的有效性检查
本篇将对"1=3""&5"这样无法求值的不正确的表达式进行检查. 将检查如下这些问题.●为无法赋值的表达式赋值(例:1 = 2 + 2)●使用非法的函数 ...
- iOS开发系列--Swift语言
概述 Swift是苹果2014年推出的全新的编程语言,它继承了C语言.ObjC的特性,且克服了C语言的兼容性问题.Swift发展过程中不仅保留了ObjC很多语法特性,它也借鉴了多种现代化语言的特点,在 ...
- iOS开发系列--C语言之基础知识
概览 当前移动开发的趋势已经势不可挡,这个系列希望浅谈一下个人对IOS开发的一些见解,这个IOS系列计划从几个角度去说IOS开发: C语言 OC基础 IOS开发(iphone/ipad) Swift ...
- iOS开发系列--C语言之构造类型
概述 在第一节中我们就提到C语言的构造类型,分为:数组.结构体.枚举.共用体,当然前面数组的内容已经说了很多了,这一节将会重点说一下其他三种类型. 结构体 枚举 共用体 结构体 数组中存储的是一系列相 ...
随机推荐
- 从0开始搭建SQL Server AlwaysOn 第四篇(配置异地机房节点)
从0开始搭建SQL Server AlwaysOn 第四篇(配置异地机房节点) 第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www ...
- 看完SQL Server 2014 Q/A答疑集锦:想不升级都难!
看完SQL Server 2014 Q/A答疑集锦:想不升级都难! 转载自:http://mp.weixin.qq.com/s/5rZCgnMKmJqeC7hbe4CZ_g 本期嘉宾为微软技术中心技术 ...
- ASP.NET Core HTTP 管道中的那些事儿
前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...
- HTML DOM 对象
本篇主要介绍HTML DOM 对象:Document.Element.Attr.Event等4个对象. 目录 1. Document 对象:表示文档树的根节点,大部分属性和方法都是对元素进行操作. 2 ...
- Android注解使用之注解编译android-apt如何切换到annotationProcessor
前言: 自从EventBus 3.x发布之后其通过注解预编译的方式解决了之前通过反射机制所引起的性能效率问题,其中注解预编译所采用的的就是android-apt的方式,不过最近Apt工具的作者宣布了不 ...
- VC中的MFC到底是什么?
1. 微软基础类库(英语:Microsoft Foundation Classes,简称MFC)是一个微软公司提供的类库(class libraries),以C++类的形式封装了Windows API ...
- Android—Volley:接收服务端发送的json数据乱码问题解决
new JsonObjectRequest中重写方法parseNetworkResponse,内容如下: /** * 重写此方法不会导致乱码 */ @Override protected Respon ...
- windows 7(32/64位)GHO安装指南(U盘引导篇)~
上一篇我们说了怎么制作U盘启动盘,那么这一篇让我们来看看如何进行正确的U盘引导启动. 现在的个人计算机一般分为台式机和笔记本,由于各厂商的喜好不同(开玩笑的啦),所以对于主板的BIOS设置各所不同.进 ...
- Linux 利用Google Authenticator实现ssh登录双因素认证
1.介绍 双因素认证:双因素身份认证就是通过你所知道再加上你所能拥有的这二个要素组合到一起才能发挥作用的身份认证系统.双因素认证是一种采用时间同步技术的系统,采用了基于时间.事件和密钥三变量而产生的一 ...
- DTO – 服务实现中的核心数据
在一个Web服务的实现中,我们常常需要访问数据库,并将从数据库中所取得的数据显示在用户页面中.这样做的一个问题是:用于在用户页面上展示的数据和从数据库中取得的数据常常具有较大区别.在这种情况下,我们常 ...