项目github地址及源码:
https://github.com/yunwei37/tryC
通常我们说的 “编译器” 是一种计算机程序,负责把一种编程语言编写的源码转换成另外一种计算机代码,后者往往是以二进制的形式被称为目标代码(object code)。这个转换的过程通常的目的是生成可执行的程序。
而解释器是一种计算机程序,它直接执行由编程语言或脚本语言编写的代码,它并不会把源代码预编译成机器码,而是一行一行地分析源代码并且直接执行,相对编译器而言可能效率较为低下,但实现也相对简单,并且容易在不同的机器上进行移植(比如x86和mips指令集的机器)。
编译器从源码翻译为目标代码大致需要这样几个步骤,每个步骤都依赖于上一个步骤的结果:
编译器对源程序进行阅读,并将字符序列,也就是源代码中一个个符号收集到称作记号(token)的单元中;比如:
```c
num = 123.4;
```
这样个赋值语句中,变量num算是一个token,“=”符号算是一个token,“123.4”算是一个token;每个token有自己的类别和属性,比如“123.4”的类别是数字,属性(值)是123.4
语法分析指将词法分析得到的标记流(token)进行分析,组成事先定义好的有意义的语句,这与自然语言中句子的语法分析类似。通常可以用抽象语法树表示语法分析的结果,比如赋值语句:
```c
num = 123.4 * 3;
```
可以用这样一个抽象语法树来表示:
```mermaid
graph TD
= --> num
= --> *
* --> 123.4
* --> 3
```
```c
num = 123.4 * 3;
```
为例,假如我们是第一次在这里遇见“num”,就将num的名称字符串“num” 和当前计算出来的初始值370.2插入符号表中,当下次再遇见num时。我们就知道它是一个数字,已经初始化完毕,并且当前值是370.2;
解释器与编译器仅在代码生成阶段有区别,而在前三个阶段如词法分析、语法分析、语义分析基本是一样的。
当然,已经有许多工具可以帮助我们处理阶段1和2,如 flex 用于词法分析,bison 用于语法分析;但它们的功能都过于强大,屏蔽了许多实现上的细节,对于学习构建编译器帮助不大,所以我们要完全手写这些功能。
(实际上完成一个可以跑起来的解释器并不难,而且还是一件很有成就感的事,不是嘛?)
从上面可以看出,我们的tryC解释器需要这三个模块:
需要这两个数据结构(用来在阶段之间保存或传递值):
在了解过这些之后,我们先来大概看看代码的基本结构:
(从上往下在代码中依次对应,“...”表示省略的相关代码,在后续文章中会详细讲解)
#include <stdio.h>
...
typedef struct symStruct {
int type;
char name[MAXNAMESIZE];
double value;
..........
} symbol;
symbol symtab[SYMTABSIZE]; // 符号表
int symPointer = 0;
char* src, * old_src; // 当前分析的源代码位置指针
// tokens 的枚举类型
enum {
Num = 128, Char, Str, Array, Func,
........
};
// token 的表示形式
int token; // current token type
union tokenValue {
symbol* ptr;
double val;
} token_val;
// 获取输入流中的下一个记号:
void next() {
char* last_pos;
while (token = *src) {
++src;
if(token == AAA ){
.....
}else if(token == BBB ){
.....
}
}
}
// 匹配一个记号,并获取下一个token:
void match(int tk) {
if (token == tk) {
next();
}
else { // 遇到了一个错误
exit(-1);
}
}
// 计算表达式的值:
double expression(){}
double factor(){}
double term(){}
// 计算布尔表达式的值:
int boolOR();
int boolAND();
int boolexp();
// 执行一个语句;
double statement();
// 执行一个函数:
double function();
int main(int argc, char** argv)
{
// 往符号表里面添加关键词
int i, fd;
src = "array func else if return while print puts read";
for (i = Array; i <= Read; ++i) {
next();
symtab[symPointer -1].type = i;
}
src = old_src = (char*)malloc(POOLSIZE); // 分配空间
....
fd = open(*argv, 0); // 打开读取文件
read(fd, src, POOLSIZE - 1);
src[i] = 0;
close(fd);
next();
while (token != 0) { // 一条一条语句执行
statement();
}
return 0;
}
可参照github源码查看(如果觉得写得还行麻烦您帮我点个star哦)
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。