• 编译器开发系列--Ocelot语言5.表达式的有效性检查


    本篇将对“1=3”“&5”这样无法求值的不正确的表达式进行检查。

    将检查如下这些问题。
    ●为无法赋值的表达式赋值(例:1 = 2 + 2)
    ●使用非法的函数名调用函数(例:"string"("%d ", i))
    ●操作数非法的数组引用(例:1[0])
    ●操作数非法的成员引用(例:1.memb)
    ●操作数非法的指针间接引用(例:1->memb)
    ●对非指针的对象取值(例:*1)
    ●对非左值的表达式取地址

    具体例子以及问题的检测方法如表10.1所示,其中包括了刚才列举的问题。

    非指针类型取值操作的检查

        /*非指针类型取值操作的检查
         * 表示取值运算符(*)的DereferenceNode的处理。
         * 该方法检查取值运算符的操作数的类型是否为指针。
         */
        // #@@range/DereferenceNode{
        public Void visit(DereferenceNode node) {
        	/*
        	 * 首先,通过super.visit(node) 调用基类Visitor 的方法遍历操作数(node.expr())
    		(即检查操作数)。
        	 */
            super.visit(node);
            /*
             * 接着,调用操作数node.expr() 的isPointer 方法,检查操作数的类型是否是指针,
    			即检查是否可以进行取值。如果无法取值,则调用undereferableError 方法输出编译错误。
             */
            if (! node.expr().isPointer()) {
                undereferableError(node.location());
            }
            /*
             * 最后,调用handleImplicitAddress 方法对数组类型和函数类型进行特别处理。该处
    			理还和接下来AddressNode 的处理相关,
             */
            handleImplicitAddress(node);
            return null;
        }
    

    获取非左值表达式地址的检查

        /*获取非左值表达式地址的检查
         * 检查操作数是否为左值。表示地址运算符的AddressNode 的处理
         */
        // #@@range/AddressNode{
        public Void visit(AddressNode node) {
            super.visit(node);
            /*
             * 首先对node.expr() 调用isLvalue 方法,检查&expr 中的expr 是否是可以进行取
    			址操作的表达式。
    			ExprNode#isLvalue 是检查该节点的表达式是否能够获取地址的方法。
             */
            if (! node.expr().isLvalue()) {
                semanticError(node.location(), "invalid expression for &");
            }
            /*
             * 剩余的语句用于确定AddressNode 的类型。通常node.expr().isLoadable() 会
    			返回true,即执行else 部分的处理。&expr 的类型是指向expr 类型的指针,因此指向
    			node.expr().type() 的指针类型可以作为节点整体的类型来使用。
             */
            Type base = node.expr().type();
            /*
             * 在将puts 的类型设置为指向函数的指针的同时,还必须将&puts 的类型也设置为指向函
    			数的指针。
    			node.expr() 的类型是数组或函数的情况下进行特别处理,使得&puts 的类型
    			和puts 的类型相一致。
             */
            if (! node.expr().isLoadable()) {
                // node.expr.type is already pointer.
                node.setType(base);
            }
            else {
                node.setType(typeTable.pointerTo(base));
            }
            return null;
        }
    

    隐式的指针生成

    单个数组类型或函数类型的变量表示数组或函数的地址。例如,假设变量puts 的类型为函数类型(一般称为函数指针),那么puts 和&puts 得到的值是相同的。

        /*
         * handleImplicitAddress 方法将数组类型或函数类型转换为了指向
    		数组或函数类型的指针,即隐式地生成指针类型。
         */
        private void handleImplicitAddress(LHSNode node) {
            if (! node.isLoadable()) {
                Type t = node.type();
                if (t.isArray()) {
                    // int[4] ary; ary; should generate int*
                    node.setType(typeTable.pointerTo(t.baseType()));
                }
                else {
                    node.setType(typeTable.pointerTo(t));
                }
            }
        }
    

    puts 是指向函数的指针,因此它的取值运算*puts 的结果是函数类型,但这样又会隐式地转换为指向函数的指针。*puts 还是指向函数的指针,因此仍然可以进行取值运算,仍然会转换为指向函数的指针。像这样可以无限重复下去。所以C 语言中“&puts”“puts”“*puts”“**puts”“***puts”的值都是相同的。

  • 相关阅读:
    PHP多进程编程
    2013年中国数据库大会PPT
    python学习笔记
    策略分析方法论
    Linux操作系统下定时发送邮件
    PHP初学
    Linux操作下的进程管理利器 Supervise
    Hive中小表与大表关联(join)的性能分析zz
    工作杂记4
    PostgreSQL 13 源码安装【转载】 规格严格
  • 原文地址:https://www.cnblogs.com/joey-hua/p/6208655.html
Copyright © 2020-2023  润新知