从《自制编译器》来看看编译器是如何编译文件的!

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

从《自制编译器》来看看编译器是如何编译文件的!
从《自制编译器》来看看编译器是如何编译文件的!
从《自制编译器》来看看编译器是如何编译文件的!


从《自制编译器》来看看编译器是如何编译文件的!
从《自制编译器》来看看编译器是如何编译文件的!


关注德州仪器公开课 


从《自制编译器》来看看编译器是如何编译文件的!


以上是关于从《自制编译器》来看看编译器是如何编译文件的!的主要内容,如果未能解决你的问题,请参考以下文章

小视频:一个自制语言编译器的诞生

从0开始自制解释器——实现简单的加法计算器

开发自制语言Monkey编译器:实现复杂算术表达式的执行

从0开始自制解释器——综述

Android Studio编译错误:java.lang.UnsatisfiedLinkError,大神来看看啊

UNITY如何使用自制DLL?