热门标签 | 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;即我们自己的虚拟机。


推荐阅读
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 本文详细介绍了在Linux虚拟化部署中进行VLAN配置的方法。首先要确认Linux系统内核是否已经支持VLAN功能,然后配置物理网卡、子网卡和虚拟VLAN网卡的关系。接着介绍了在Linux配置VLAN Trunk的步骤,包括将物理网卡添加到VLAN、检查添加的VLAN虚拟网卡信息以及重启网络服务等。最后,通过验证连通性来确认配置是否成功。 ... [详细]
  • C语言自带的快排和二分查找
    Author🚹:CofCaiEmail✉️:cai.dongjunnexuslink.cnQQ😙:1664866311personalPage&#x ... [详细]
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社区 版权所有