程序员人生 网站导航

程序设计基石与实践系列之写一个C语言编译器

栏目:php教程时间:2016-03-01 08:18:33

动手编写1个编译器,学习1下较为底层的编程方式,是1种学习计算机究竟是如何工作的非常有效方法。

编译器通常被看做是10分复杂的工程。事实上,编写1个产品级的编译器也确切是1个庞大的任务。但是写1个小巧可用的编译器却不是这么困难。

秘诀就是首先去找到1个最小的可用工程,然后把你想要的特性添加进去。这个方法也是Abdulaziz Ghuloum在他那篇著名的论文“1种构造编译器的捷径”里所提到的办法。不过这个办法确切可行。你只需要依照这篇论文中的第1步来操作,就能够得到1个真正可用的编译器!固然,它只能编译程序语言中的非常小的子集,但是它确切是1个真实可用的编译器。你可以随便的扩大这个编译器,然后从中学到更多更深的知识。

遭到这篇文章的鼓舞,我就写了1个C编译器。从某种意义上来讲这比写1个scheme的编译器要困难1些(由于你必须去解析C那复杂的语法),但是在某些方面又很便利(你不需要去处理运行时类型)。要写这样1个编译器,你只需要从你那个可用的最小的编译器开始。

对我写的编译器来讲,我把它叫 babyc,我选了这段代码来作为我需要运行的第1个程序:

int main() { return 2; }

没有变量,没有函数调用,没有额外的依赖,乃至连if语句,循环语句都没有,1切看起来是那末简单。

我们首先需要解析这段代码。我们将使用 Flex 和 Bison 来做到这点。这里有怎样用的例子可以参考,幸亏我们的语法是如此简单,下面就是词法分析器:

"{" { return {; } "}" { return }; } "(" { return (; } ")" { return ); } ";" { return ;; } [0⑼]+ { return NUMBER; } "return" { return RETURN; } "int" { return TYPE; } "main" { return IDENTIFIER; }

这里是语法分析器:

function: TYPE IDENTIFIER ( ) { expression } ; expression: RETURN NUMBER ; ;

终究,我们需要生成1些汇编代码。我们将使用32位的X86汇编,由于它非常的通用而且可以很容易的运行在你的机器上。这里有X86汇编的相干网站。

下面就是我们需要生成的汇编代码:

.text .global _start # Tell the loader we want to start at _start. _start: movl $2,%ebx # The argument to our system call. movl $1,%eax # The system call number of sys_exit is 1. int $0x80 # Send an interrupt

然后加上上面的词法语法分析代码,把这段汇编代码写进1个文件里。恭喜你!你已是1个编译器的编写者了!

Babyc 就是这样诞生的,你可以在这里看到它最开始的模样。

固然,如果汇编代码没办法运行也是枉然。让我们来用编译器生成我们所希望的真实的汇编代码。

# Heres the file we want to compile. $ cat return_two.c #include int main() { return 2; } # Run the compiler with this file. $ ./babyc return_two.c Written out.s. # Check the output looks sensible. $ cat out.s .text .global _start _start: movl $2, %ebx movl $1, %eax int $0x80

非常棒!接着让我们来真实的运行1下编译以后代码来确保它能得到我们所想的结果。

# Assemble the file. We explicitly assemble as 32-bit # to avoid confusion on x86_64 machines. $ as out.s -o out.o -⑶2 # Link the file, again specifying 32-bit. $ ld -m elf_i386 -s -o out out.o # Run it! $ ./out # What was the return code? $ echo $? 2 # Woohoo!

我们踏出了第1步,接下去怎样做就全看你了。你可以依照那篇文章所指点的全部做1遍,然后制作1个更加复杂的编译器。你需要去写1个更加精致的语法树来生成汇编代码。接下去的几步分别是:(1)允许返回任意的值(比如,return 3; 1些可履行代码);(2)添加对“非”的支持(比如,return ~1; 1些可履行代码)。每个额外的特性都可以教你关于C语言的更多知识,编译器究竟是怎样履行的,和世界上其他编写编译器的人是如何想的。

这是构建 babyc 的方法。Babyc 现在已具有了if语句,循环,变量和最基础的数据结构。欢迎你来check out它的代码,但是我希望看完我的文章你能够自己动手写1个。

不要惧怕底层的1些事情。这是1个非常奇妙的世界。


------分隔线----------------------------
------分隔线----------------------------

最新技术推荐