• 编译器开发系列--Ocelot语言3.类型名称的消解


    “类型名称的消解”即类型的消解。类型名称由TypeRef 对象表示,类型由Type 对象表示。类型名称的消解就是将TypeRef 对象转换为Type 对象。

    TypeResolver 类的处理仅仅是遍历抽象语法树,发现TypeRef 的话就从叶子节点开始将其转换为Type 类型。类型和变量的不同之处在于没有作用域的嵌套(作用域唯一),因此没
    有必要使用栈。
    【TypeRef 对象和Type 对象的对应关系保存在TypeTable 对象中。】

    其中Type为类型的定义。struct point { int x; int y; }; 是类型的定义。

    TypeRef为类型的名称。struct point 是类型的名称,之所以特意将Type 类和TypeRef 类分开,是因为在类型定义之前就可以编写用到了该类型的代码。也就是说,可以编写如下所示的代码,C 语言中是不可以编写这样的代码的:

    	struct s var;
    	struct s {
    		int memb;
    	};
    

    类型名称的消解入口:

        /*入口
         * 
         */
        // #@@range/resolveProgram{
        public void resolve(AST ast) {
        	/*
        	 * 首先调用defineTypes 方法,根据代码中定义的类型生成Type 对象,并保存到
    			TypeTable 对象中。通过import 导入的类型定义也在这里处理。
        	 */
            defineTypes(ast.types());
            /*类型和抽象语法树的遍历.
             * 但defineTypes 方法不处理结构体成员的类型等TypeRef 对象。将抽象语法树中已有
    			的TypeRef 转换成Type 的处理将在下面的foreach 语句中执行。如果这两部分处理不分开进
    			行的话,在处理递归的类型定义时程序会陷入死循环。
    			
    			ast.types()--源文件内外的类型定义
             */
            // #@@range/resolveProgram_core{
            for (TypeDefinition t : ast.types()) {
                t.accept(this);
            }
            /*
             * 第2 个foreach 语句将使用import 从文件外部读入的定义、全局变量以及函数等所有剩余
    			的TypeRef 转换为Type。
    			
    			ast.entities()--用import 导入的变量和函数的声明,以及源文件内的变量和函数的定义
             */
            for (Entity e : ast.entities()) {
                e.accept(this);
            }
            /*
             * 上面两个for循环遍历在源文件内外定义的所有类型、变量、函数,将其中所包含的TypeRef 对象
    			全部转换为Type 对象。
             */
            // #@@}
        }
    

    首先对ast.types(),即StructNode(结构体定义)、UnionNode(联合体定义)、TypedefNode(用户类型定义)执行defineTypes:

        /*类型的声明.
         * defineTypes 是将类型定义添加到TypeTable 对象的方法
         */
        // #@@range/defineTypes{
        private void defineTypes(List<TypeDefinition> deftypes) {
        	/*
        	 * 使用foreach 语句将deftypes 中的TypeDefinition 对象逐个取出, 将def.
    			typeRef() 和def.definingType() 关联成对, 用typeTable.put 方法添加到
    			typeTable 中。def.typeRef() 返回的是该TypeDefinition 对象要定义的类型的
    			TypeRef(类型名称)。def.definingType() 返回的是该TypeDefinition 对象要定义的
    			Type(类型)。
        	 */
            for (TypeDefinition def : deftypes) {
            	/*
            	 * 但如果typeTable.isDefined() 为true 的话,说明这个TypeRef 已经存在,这种情
    				况下取消添加处理并输出错误消息。
            	 */
                if (typeTable.isDefined(def.typeRef())) {
                    error(def, "duplicated type definition: " + def.typeRef());
                }
                else {
                	/*
                	 * TypeDefinition 类是抽象类, 实际生成的实例是TypeDefinition 的子类
    					StructNode、UnionNode、TypedefNode。StructNode 表示结构体的定义,UnionNode
    					表示联合体的定义,TypedefNode 表示typedef 语句。
    					StructNode#definingType:
    					public Type definingType() {
    						return new StructType(name(), members(), location());
    					}
    					
    					调用TypeTable#put 方法将生成的StrcutType 对
    					象添加到TypeTable 对象中。TypeTable 对象的内部保存有HashMap 对象, 因此
    					TypeTable#put 方法只需简单地调用HashMap#put 即可。
                	 */
                    typeTable.put(def.typeRef(), def.definingType());
                }
            }
        }
    

    把上面三种类型的名称和类型都保存在typeTable中,注意typeTable初始化的时候已经自动把所有基本类型都put进去了。然后第一个for循环的三个visit方法:

        // #@@range/StructNode{
        public Void visit(StructNode struct) {
            resolveCompositeType(struct);
            return null;
        }
        // #@@}
    
        // #@@range/UnionNode{
        public Void visit(UnionNode union) {
            resolveCompositeType(union);
            return null;
        }
        // #@@}
    
        // #@@range/TypedefNode{
        public Void visit(TypedefNode typedef) {
            bindType(typedef.typeNode());
            bindType(typedef.realTypeNode());
            return null;
        }
        // #@@}
    

    接着:

        public void resolveCompositeType(CompositeTypeDefinition def) {
            CompositeType ct = (CompositeType)typeTable.get(def.typeNode().typeRef());
            if (ct == null) {
                throw new Error("cannot intern struct/union: " + def.name());
            }
            for (Slot s : ct.members()) {
                bindType(s.typeNode());
            }
        }
    
        /*
         * 首先,用TypeNode#isResolved 方法检查是否已经完成了转换,如果已经完成,则即
    		刻使用return 结束处理。如果还未转换,用n.typeRef() 从TypeNode 中取出TypeRef,
    		再用typeTable.get 转换为Type 对象, 然后将此Type 对象用n.setType 设置到
    		TypeNode 中。
         */
        // #@@range/bindType{
        private void bindType(TypeNode n) {
            if (n.isResolved()) return;
            n.setType(typeTable.get(n.typeRef()));
        }
    

    也很简单,resolveCompositeType是针对每种类型的成员的类型检查,关键的类是TypeNode,从它里面获取TypeRef(类型的名称),再通过类型的名称从typeTable获取已有的类型的定义。然后获取到当前类型的所有的成员变量,再将这个成员变量的类型的名称和定义通过bindType方法绑定起来。typeTable实际上是起到一个中转站的作用。

    第二个for循环是将除了上面三种类型的所有剩余的TypeRef 转换为Type。比如:

        /*
         * 变量定义的类型消解.
         */
        // #@@range/DefinedVariable{
        public Void visit(DefinedVariable var) {
        	/*
        	 * TypeRef 对象基本上都存放在TypeNode 对象中。TypeNode 是成对地保存TypeRef 和
    			Type 的对象,其目的在于简化TypeResolver 类的代码。
        	 */
            bindType(var.typeNode());
            if (var.hasInitializer()) {
                visitExpr(var.initializer());
            }
            return null;
        }
    

    还有重要的函数类型:

        /*
         * 函数定义的类型消解.
         */
        // #@@range/DefinedFunction{
        public Void visit(DefinedFunction func) {
        	/*
        	 * 在函数定义中,如下这些地方存在TypeRef。
    			1. 返回值的类型
    			2. 形参的类型
    			3. 函数体的代码中
        	 */
            resolveFunctionHeader(func);
            visitStmt(func.body());
            return null;
        }
    
        private void resolveFunctionHeader(Function func) {
        	/*
        	 * resolveFunctionHeader 方法的第1 行用于处理返回值的类型。func.typeNode()
    			返回保存有返回值类型的TypeNode 对象,再调用bindType 方法将返回值的类型从
    			TypeRef 转换为Type。
        	 */
            bindType(func.typeNode());
            /*
             * resolveFunctionHeader 方法从第2 行开始都是对形参进行的处理。用foreach 语句
             * 对func.parameters() 进行遍历,取出表示形参的Parameter 对象。然后用param.
    			typeNode() 取出Parameter 对象中的TypeNode 对象,将TypeRef 转换为Type。
             */
            for (Parameter param : func.parameters()) {
                // arrays must be converted to pointers in a function parameter.
            	/*
            	 * 只有在将形参的TypeRef 转换为Type 时使用了TypeTable 类的getParamType 方法。
    				它和通常的get 方法的区别在于数组的TypeRef 会被转换为指针的Type。C 语言(C♭)中形
    				参类型是数组的情况下完全等同于指针类型,因此在此处统一成为指针类型。
            	 */
                Type t = typeTable.getParamType(param.typeNode().typeRef());
                param.typeNode().setType(t);
            }
        }
    

    首先调用resolveFunctionHeader方法,里面第一行是绑定函数的返回类型,然后一个for循环绑定函数的所有形参类型。然后再调用visitStmt(func.body());绑定函数体的所有类型:

        public Void visit(BlockNode node) {
            for (DefinedVariable var : node.variables()) {
                var.accept(this);
            }
            visitStmts(node.stmts());
            return null;
        }
    
  • 相关阅读:
    什么叫工作到位?
    SQL中PIVOT 使用
    SQL中ROW_NUMBER() 使用
    Fiddler 抓包工具总结
    设计模式之单例模式
    数据库脏读、不可重复读、幻读
    SQL查询优化《四》:临时表和表变量的使用
    SQL查询优化《三》:少做重复的工作
    SQL查询优化《二》:只返回需要的数据
    SQL查询优化《一》:SQL语句执行顺序
  • 原文地址:https://www.cnblogs.com/joey-hua/p/6194664.html
Copyright © 2020-2023  润新知