从《自制编译器》来看看编译器是如何编译文件的!
Posted 嵌入式ARM
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从《自制编译器》来看看编译器是如何编译文件的!相关的知识,希望对你有一定的参考价值。
最近,看了一本叫《自制编译器》的书,图书的作者是一位日本人,写的很仔细。其实,图灵出版社还引入了一些列的自制书籍,包括《自制cpu》、《自制操作系统》等等,有兴趣的童鞋都可以找来看看。事实上现在网上这类的资源很多了,只要你想学,基本上都可以满足你的需求。
通常情况下,编译文件分为这几个步骤:
词法分析
语法分析
语义分析
中间代码生成
代码生成与优化
词法分析比较简单,主要就是读取文本文件,将文件中的字符串进行归类处理,比如说哪些是关键字、哪些是数字、哪些是字符串、哪些是分割符等等。
语法分析稍微复杂一些,它主要是把文本字符串按照当初bnf范式的形式构建成一个合适的语法树,方便后面进行中间代码生成。
语义分析一般紧跟着语法分析进行,它主要是检查语法树中是不是存在非法的语句存在,比如变量重复出现、赋值错误、除数为0等等。
中间代码比较简单,主要是按照语法树的要求,暂时先将语法树上的block转变成四元组,或者是你自己定义的某种临时代码格式,之所以这么做是因为事实上需要将源文件转换成很多种cpu代码,因此必须要先这么做。
至此之前的这四个部分可以称之为编译器前端处理,后端处理呢就是将中间代码转变成目标cpu代码。
最后一部就是代码生成与优化,arm处理器就转变成arm代码,x86处理器就转变成x86代码,选择不同的转变方式就可以了。同样,生成代码的时候必要的优化也是不可少的,有的是基于运算速度的,有的是基于空间大小的,还有的是基于cpu乱序执行的,这部分内容就很复杂了。总之,优化的目的就是为了除去冗余代码,越快越好。
早先开发编译器是一件非常辛苦的事情,但是现在就没有那么困难了。书中介绍的工具是javacc,这是一个语法解析工具,用Java写成。可能你并不是很熟悉,你可以自己设计一个脚本语言,像perl、Python一样,也没有任何问题。
1. #include <stdio.h>
2.
3. int main() {
4.
5. return 0;
6. }
词法分析的结果是这样的,python compiler.py -s hello.c -l,
1. (SHARP, #)
2. (INCLUDE, include)
3. (LT, <)
4. (IDENTIFIER, stdio.h)
5. (GT, >)
6. (INT, int)
7. (IDENTIFIER, main)
8. (LL_BRACKET, ()
9. (RL_BRACKET, ))
10. (LB_BRACKET, {)
11. (RETURN, return)
12. (DIGIT_CONSTANT, 0)
13. (SEMICOLON, ;)
14. (RB_BRACKET, })
语法分析是这样的, python compiler.py -s hello.c -p
1. ( self: Sentence None, father: None, left: None, right: None )
2. ( self: Include None, father: Sentence, left: None, right: FunctionStatement )
3. ( self: # None, father: Include, left: None, right: include )
4. ( self: include None, father: Include, left: #, right: < )
5. ( self: < None, father: Include, left: include, right: stdio.h )
6. ( self: stdio.h None, father: Include, left: <, right: > )
7. ( self: > None, father: Include, left: stdio.h, right: None )
8. ( self: FunctionStatement None, father: Sentence, left: Include, right: None )
9. ( self: Type None, father: FunctionStatement, left: None, right: FunctionName )
10. ( self: int FIELD_TYPE, father: Type, left: None, right: None )
11. ( self: FunctionName None, father: FunctionStatement, left: Type, right: StateParameterList )
12. ( self: main IDENTIFIER, father: FunctionName, left: None, right: None )
13. ( self: StateParameterList None, father: FunctionStatement, left: FunctionName, right: Sentence )
14. ( self: Sentence None, father: FunctionStatement, left: StateParameterList, right: None )
15. ( self: Return None, father: Sentence, left: None, right: None )
16. ( self: return None, father: Return, left: None, right: Expression )
17. ( self: Expression Constant, father: Return, left: return, right: None )
18. ( self: 0 _Constant, father: Expression, left: None, right: None )
生成的汇编文件又是这样的,python compiler.py -s hello.c -a,
1. .data
2. .bss
3. .lcomm bss_tmp, 4
4. .text
5. .globl main
6. main:
7. finit
8. pushl $0
9. call exit
虽然这个python还不能处理嵌套等特别复杂的语法,但是用仅仅1500行的code就能做到这样其实已经是难得可贵了。有兴趣的同学可以去试一试,也可以看看这个compiler.py文件是怎么写的。
关注嵌入式ARM
关注21ic官方微信
关注 电源Fan
关注德州仪器公开课
以上是关于从《自制编译器》来看看编译器是如何编译文件的!的主要内容,如果未能解决你的问题,请参考以下文章