语义分析- C-- 语言
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就生成好了,符号表如下所示。
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;}
}