• 编译器开发系列--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);
            }
        }
    
  • 相关阅读:
    【LeetCode】543. 二叉树的直径
    红色的眼睛黑色的心
    WinForm
    Windows地址栏的妙用
    C#
    WPF
    配置Notepad++万能调试
    盗取连接你wifi的人的qq
    Windows去除开始菜单图标背景
    解决Windows下文件无法删除的问题
  • 原文地址:https://www.cnblogs.com/joey-hua/p/6203706.html
Copyright © 2020-2023  润新知