关于语义分析的简单总结

预备知识

  1. 编译器的前端是由三个模块和两个核心数据结构(记号流,抽象语法树)组成的,
  2. 编译器处于一个流水线的结构,阶段无关性(只考虑把每一个模块的输入输出)
  3. 语义分析只依赖于前一阶段的抽象语法树
  4. 语义分析也称为类型检查。上下文相关分析。负责检查程序(抽象语法树)的上下文相关的属性:
    1. 变量在使用前先声明
    2. 每个表达式都有何时的类型
    3. 函数调用和函数的定义一致
    4. ………………
      举个例子
void f(int *p)
{
    X += 4:
    Cal(X);
    break;
}

编译器报出x,cal没有定义的错误,没有找到循环的错误,这就是所谓的上下文相关,不仅仅是检查当前

所以语义分析要完成的工作就是针对给定的一段代码,去找出所有的语义错误,如果出错了,要给出清晰的诊断信息反馈给程序员,程序员根据出错信息改错

语义分析器接受一个抽象语法树的输入,这棵树必然满足语法正确,还有一个输入,就是程序语言的合法规则(表明什么是合法的,什么是非法的),输出Yes或者NO,如果是Yes,那么就要产生中间代码(这个阶段过后,程序就不应包含任何语法和语义错误),如果是NO,就要给出错误信息

如何定义程序语言的语义?

传统上,大部分的程序设计语言都采用自然语言来表达程序语言的语义
例如,对于“+”运算
要求左右操作数都必须是整形数,如e1 + e2, 对于语法分析,+号两边操作数是不管的,而语义分析就要检查是否为整形数
这个自然语言是我们学习一门语言的所需要的,比如一本编程书上

但编译器的实现者必须对语言中的语义规定有全面的理解,比如加号两边可以是浮点数和整形数

那么如何能够正确高效地实现?

给出下面文法举个例子
定义加法左右两边只能是整数

E->N
   | true
   | false
   | E + E
   | E && E

类型合法的程序

3 + 4
False && true

类型不合法的程序

3 + true
True + false

对这个语言,语义分析的任务是:对给定一个表达式e,写一个函数check(e)
返回一个表达式e的类型;若类型不合法,则报错
如3 + 4这个表达式返回int, 3+true这个表达式就会出错

下面对应抽象语法树的后序遍历的伪代码

enum type {int, bool};
enum type check (exp_t e)
{
    switch(e->kind)
    case exp_int: return int;
    case exp_true: return true;
    case exp_false: return false;
    case exp_add: t1 = check(e->left);//左
                  t2 = check(e->right);//右
                  if(t1 != int || t2 != int) //根
                     perror(“error”);
                  else return int;
    case exp_and: t1 = check(e->left);
                  t2 = check(e->right);
                  if(t1 != bool || t2 != bool)
                     perror(“error”);
                   else return bool
}
符号表

对于类型检查的有一个很重要的数据结构,符号表(key, value)
符号表:
用来存储程序中的变量相关信息
类型
作用域
访问控制信息(如pubic ,private之类的,文件之间的访问)
必须非常高效,因为程序中的变量规模会很大
如int x, bool y
table
x:int …..
y:bool ….

符号表要有创建,插入,查找的接口功能,具体实现和所选择语言相关
为了高效,可以使用哈希表等数据结构来实现呢符号表,
为了节约空间,也可以使用红黑树等平衡树

语义分析可能会出现的共性问题:

  • 类型相容性:
    类型检查问题往往归结为判断两个类型是否相等t1==t2? 在实际的语言中,这往往是个需要谨慎处理的复杂问题
    如:
    1. 对采用名字相等的语言,可直接比较
      对采用结构相等的语言(如结构体),需要递归比较比较各个域
    2. 面向对象的继承,需要维护类型间的继承关系
  • 错误诊断:编译器要报出语义错误,这些信息要很清晰地给出
    (1) 要给出极可能准确的错误信息
    (2) 要给出尽可能多的错误信息
    (3) 要给出尽可能准确的出错位置,程序代码的位置要从缱绻保留并传递归来(即词法分析的行号信息要传递到语义分析)
  • 代码翻译:生成中间表示(或目标代码),现代编译器中的语义分析模块,除了做语义分析外,还要负责生成中间代码或目标代码,代码生成的过程也同样是对树的某种遍历
    因此,语义分析往往是编译器中最庞大(代码量)最复杂(需要很多分析)的模块
阅读更多
版权声明:转载务必请标明出处,谢谢 https://blog.csdn.net/wyt734933289/article/details/53956151
个人分类: 编译原理
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭