原创

编译器中的语义分析

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://daichen.blog.csdn.net/article/details/100097850

最近一直在看编译原理方面的书,之前着重研究过词法解析和语法解析,这一次因为工作原因比较侧重语义分析。简单来说,语义分析是发生在词法和语法解析之后,输入是Concrete Syntax Tree又名Parse Tree。语义分析器对Parse Tree进行分析,同时可以产生Abstract Syntax Tree(AST)。AST即忽略了语法细节的语法树,可能大家更熟悉一些。AST一般作为编译器前端的最终产物,传递给中端或者后端,产生最终的目标代码。Parse Tree则更为“详细”一些,具体来说就是与grammar一一对应,所有Token都能在Parse Tree上找到。比如函数调用 f(a, b),括号和逗号都会被保留。所以,语义分析时发现任何问题,都可以在错误信息中包含出错的确切位置。明确了语义分析在整个编译器前端中的位置,下面就来具体说说语义分析具体要做什么和怎么做。


1.语义分析的内容

语义分析最重要的工作就是类型检查。比如两个变量a和b,表达式a+b是否合法取决于a和b的类型,比如数字和字符串是合法的,布尔值是非法的。大家可能会想,为什么不直接在grammar中声明好呢?

  1. grammar中实现类型检查会导致语法与逻辑混合在一起,难以维护。
  2. 类型信息可能需要动态获取,比如SQL语句中的列类型,或者运算符被重载。

除了Type Checking,语义分析一般还会做Label Checking(变量是否在之前已定义),Scope Checking(函数是否可以在这里被调用)等等。函数的调用参数顺序和个数也经常在语义分析中实现,原因大概是因为反正都要对参数做类型检查,还不如简化grammar,在类型检查时对函数的入参一并检查。


2.语义分析的方法

仔细思考一下,实现语义分析好像有很多特殊case要处理,比如SQL语句的Scope Checking,SUM()不能在WHERE 中调用,自然的想法是在碰到SUM时往上寻找是否有WHERE结点,一直到Parse Tree的根结点。碰到种种实现问题,再去读编译原理的课本就会发现,前人已经在研究透彻后提取出了强大的抽象,第一个例子就是这里要说的Scope作用域。

2.1 Scope管理

前面说到的语义分析的主要内容,Label Checking和Scope Checking其实都可以通过统一的Scope管理来解决。具体来说就是始终维护一个当前结点可以使用的Symbol的表,只要是不在里面的就都是非法的。比如在访问函数f在Parse Tree上的结点时,发现参数a并没有在当前Scope的符号表里,那么这就是一个非法引用。

2.2 类型系统

下面要解决的另一大问题就是Type Checking。要做类型检查,首先得定义好一个符号系统。对于静态语言来说相对容易一些,因为所有类型都在编译时确定了。这里要提到的就是第二个强大的抽象——Type Constructor。类型系统包含两部分,一是基本类型,二是构造器+基本类型,构造器可以是类、数组、函数、运算符等等。这个抽象的作用就在类型检查模型之中。具体要做类型检查时,一般采取Bottom-up自底向上的方式,每个结点都向上返回一个检查的结果,于是类型检查就逐级汇总到根结点。比如表达式g(f(a), b),首先a和b会返回自身的类型,a返回给f,f返回其返回值的类型给g,g检查f返回值类型和b,从而得出结论。

文章最后发布于: 2019-08-27 13:34:35
展开阅读全文
0 个人打赏
私信求帮助

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

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览