C-- V1.0

E -> n

   |   true

   |   false

   |   E + E

   |   E && E

类型合法的程序:

  • 3 + 4
  • false && true

类型不合法的程序:

  • 3 + true
  • true + false 

对这个语言,语义分析的任务是:对给定的一个表达式e,写一个函数type check(e);返回表达式e的类型;若类型不合法,则报错。

针对C -- 语言构造的类型检查伪代码算法如下:

(ps:其中的数据结构定义可参考抽象语法树的定义(C语言版)_青衫客36的博客-CSDN博客

enum type {INT, BOOL};

enum type check_exp(Exp_t e)
{
    switch (e->kind)
    {
    case EXP_INT:   return INT;
    case EXP_TRUE:  return BOOL;
    case EXP_FALSE: return BOOL;

    case EXP_ADD:   t1 = check_exp(e->left);
                    t2 = check_exp(e->right);
                    if (t1 != INT || t2 != INT)
                        error("type mismatch");
                    else return INT;

    case EXP_AND:   t1 = check_exp(e->left);
                    t2 = check_exp(e->right);
                    if (t1 != BOOL || t2 != BOOL)
                        error("type mismatch");
                    else return BOOL;
    default:
        break;
    }
}

C-- V2.0

接下来我们把这个语言稍微扩展一下,我们加上变量声明,看看对变量声明该如何处理。

P ->  D E

D -> T id; D

   |

T ->  int

   |    bool

E -> n

   |    id

   |    true

   |    false

   |    E + E

   |    E && E

注:P为程序,D为变量声明,E为表达式,T为数据类型,上述产生式中的id为变量,n为常数

类型合法的程序1:

int x;

x + 4

x的类型在第一行声明,需要做一个向上引用,体现了上下文相关分析的意思。 

类型合法的程序2:

bool y;

false && y

类型不合法的程序1:

x + 3

变量在使用之前没有声明。

类型不合法的程序2:

int x;

x + false

x是整形,整形+布尔类型是不合法的。 

此时的类型检查算法变更如下:

enum type {INT, BOOL};
Table_t table;  // 符号表

// 检查程序
enum type check_prog(Dec_t d, Exp_t e)
{
    // 递归的对声明部分做检查,检查完之后给table赋值,初始化全局符号表
    table = check_dec(d);
    return check_exp(e); // 整个程序类型为内部表达式类型
}

// 检查变量声明
Table_t check_dec(Dec_t d)
{
    foreach(T id ∈ d)
        table_enter(table, id, T) // 将每个变量名、变量类型插入符号表
}

// 检查表达式
enum type check_exp(Exp_t e)
{
    switch (e->kind)
    {
        case EXP_ID:
            t = Table_lookup(table, id);
            if (id not exist)
                error("id not found");
            else return t;
    }
}

此处的符号表可以理解为key -> value 的映射 [ id -> type ]

假设给定如下C--程序:

int x;
bool y;
4 + x;

其对应的语法树如下: 

首先程序先来检查左子树(变量声明d的部分),对其中的每一个变量声明去填符号表,做完第一部分,这个table就生成好了,符号表如下所示。

符号表table
x int
y bool

然后有了这个全局变量table,我们根据这个符号表继续来看check_exp(e)(即接下来对表达式的类型检查是怎么做的)

对于表达式中的变量x,我们先做一个表的查找操作,如果id在这个表里面没有查到的话,直接报错,此时对应这种情况,即有一个变量正在使用,但是这个变量没有声明过。否则,说明变量x存在,那么一定会找到类型t,然后把t返回回去即可。(ps:具体可见上面的代码)

总结一下,这个类型检查算法其实也是另外一种后序遍历,首先对于整个程序来说,先遍历其左子树(所有的声明部分),这个声明部分构造一张全局的表,然后在递归调用右子树的时候,变量可以根据这个表进行查找,可以帮助右边表达式做类型检查。

所谓上下文相关检查,核心的概念就是这张符号表,即把上下文相关的信息存储到这里面,接下来用的时候再去表里面来查。

C-- V3.0

接下来我们再增加对于语句的处理。

P -> D E

D -> T id; D

   |

T -> int

   |   bool

S -> id = E

   |   printi(E)

   |   printb(E)

E -> n

   |   id

   |   true

   |   false

   |   E + E

   |   E && E

ps:P为程序,D为变量声明,S为语句,T为变量类型,E为表达式 

新增的检查statement函数伪代码如下: 

void check_stm(Table_t table, Stm_t s)
{
    switch (s->kind)
    {
        case STM_ASSIGN:
            t1 = Table_lookup(s->id);
            t2 = check_exp(table, s->exp);
            if (t1 != t2)
                error("type mismatch");
            else return INT;

        case STM_PRINTI:
            t = check_exp(s->exp);
            if (t != INT)
                error("type mismatch");
            else return ;

        case STM_PRINTB:
            t = check_exp(s->exp);
            if (t != BOOL)
                error("type mismatch");
            else return ;
            
        default:
            break;
    }
}

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐