热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

自己动手制作C语言编译器(1):设计

这篇文章我们要从整体上讲解如何设计我们的C语言编译器。本系列:首先要说明的是,虽然标题是编译器,但实际上我们构建的是C语言的解释器&#

这篇文章我们要从整体上讲解如何设计我们的 C 语言编译器。

本系列:

首先要说明的是,虽然标题是编译器,但实际上我们构建的是 C 语言的解释器,这意味着我们可以像运行脚本一样去运行 C 语言的源代码文件。这么做的理由有两点:

1.解释器与编译器仅在代码生成阶段有区别,而其它方面如词法分析、语法分析是一样的。

2.解释器需要我们实现自己的虚拟机与指令集,而这部分能帮助我们了解计算机的工作原理。

编译器的构建流程

一般而言,编译器的编写分为 3 个步骤:

1.词法分析器,用于将字符串转化成内部的表示结构。

2.语法分析器,将词法分析得到的标记流(token)生成一棵语法树。

3.目标代码的生成,将语法树转化成目标代码。

已经有许多工具能帮助我们处理阶段1和2,如 flex 用于词法分析,bison 用于语法分析。只是它们的功能都过于强大,屏蔽了许多实现上的细节,对于学习构建编译器帮助不大。所以我们要完全手写这些功能。

所以我们会根据下面的流程:

1.构建我们自己的虚拟机以及指令集。这后生成的目标代码便是我们的指令集。

2.构建我们的词法分析器

3.构建语法分析器

编译器的框架

我们的编译器主要包括 4 个函数:

next()用于词法分析,获取下一个标记,它将自动忽略空白字符。

program()语法分析的入口,分析整个 C 语言程序。

expression(level)用于解析一个表达式。

eval()虚拟机的入口,用于解释目标代码。

这里有一个单独用于解析“表达式”的函数expression是因为表达式在语法分析中相对独立并且比较复杂,所以我们将它单独作为一个模块(函数)。

因为我们的源代码看起来就像是:

#include

#include

#include

#include

 

int token;            // current token

char *src, *old_src;  // pointer to source code string;

int poolsize;         // default size of text/data/stack

int line;             // line number

 

void next() {

    token = *src++;

    return;

}

 

void expression(int level) {

    // do nothing

}

 

void program() {

    next();                  // get next token

    while (token > 0) {

        printf("token is: %c\n", token);

        next();

    }

}

 

int eval() { // do nothing yet

    return 0;

}

 

int main(int argc, char **argv)

{

    int i, fd;

 

    argc--;

    argv++;

 

    poolsize = 256 * 1024; // arbitrary size

    line = 1;

 

    if ((fd &#61; open(*argv, 0)) <0) {

        printf("could not open(%s)\n", *argv);

        return -1;

    }

 

    if (!(src &#61; old_src &#61; malloc(poolsize))) {

        printf("could not malloc(%d) for source area\n", poolsize);

        return -1;

    }

 

    // read the source file

    if ((i &#61; read(fd, src, poolsize-1)) <&#61; 0) {

        printf("read() returned %d\n", i);

        return -1;

    }

    src[i] &#61; 0; // add EOF character

    close(fd);

 

    program();

    return eval();

}

上面的代码看上去挺复杂&#xff0c;但其实内容不多&#xff0c;就是读取一个源代码文件&#xff0c;逐个读取每个字符&#xff0c;并输出每个字符。这里重要的是注意每个函数的作用&#xff0c;后面的文章中&#xff0c;我们将逐个填充每个函数的功能&#xff0c;最终构建起我们的编译器。如果想一起交流的可以加这个群&#xff1a;941636044 &#xff0c;有什么问题可以群里面交流&#xff0c;群里面也有一些方便学习C语言C&#43;&#43;编程的资料可以给你利用。

这样我们就有了一个最简单的编译器&#xff1a;什么都不干的编译器&#xff0c;下一章中&#xff0c;我们将实现其中的eval函数&#xff0c;即我们自己的虚拟机。


推荐阅读
author-avatar
暮色归家
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有