构建自己的编译器(六)虚拟机之二

对,你解析了如果只是一个一个next的话,这就是将词法分析和语法分析合在一起了,因为词法分析本来就不是什么难事
语法树都没构造它怎么就可以进行变量定义了?
它是通过解析 什么enmu啦,int什么的完成的,然后按照定义完成赋值,根本不用断句,
好,即便不用断句,那么它是把参数压入栈了吗?
首先,一个主函数一个栈,当调用次函数时,要把pc压入栈,然后把pc赋给次函数,执行,完毕则弹出栈,
这个是汇编函数的,而print则从虚拟机的角度单独抽出。
一个栈stack,一个备用栈t_stack,print的时候就往栈中找这个变量,从而。。。。打印出。
还是,给它定义一个数据段,那么,虚拟指针是怎么工作的?实际上,是我们之前开辟的。
我们把编译好的三地址码放到代码段,然后指针就开始运作了。我们规定,这个机器是以字节为单位,al是一个字节,8位,
ax是2个字节,16位,地址几个字节啊?一个int里面能存4个字节呢!指针必然是int型的,四个字节,从0x0000开始执行程序。
指针呢,则每次加1?这样的话呢,就是每条指令必然占据相同的空间。除非是说,这个虚拟机能自动切割语句,对吧,实际上
指针每次都是移向下一个语句的是吧,那么一条语句有多长呢?4个字节呢?分成1个字节+1.5个字节+1.5个字节,不行,必须为
完整的字节。
关于地址,你说一个寄存器ax, 或者说一个int* ad, 你能这样赋值 ad = address, 你也可以这样赋值 *ad = value, 当然,你也
可以反着来。
看这里一段代码:
i = 0;
text[i++] = IMM;
text[i++] = 10;
text[i++] = PUSH;
text[i++] = IMM;
text[i++] = 20;
text[i++] = ADD;
text[i++] = PUSH;
text[i++] = EXIT;
pc = text;
program();
很明显test是一个int数组,然后数据与命令是紧凑放置的,pc每次增加1,program则是虚拟机的运行。
这个虚拟机是如何运行的呢?
1、next() 用于词法分析,获取下一个标记,它将自动忽略空白字符。相当于词法分析,控制token
2、program() 语法分析的入口,分析整个 C 语言程序。相当于语法分析
3、expression(level) 用于解析一个表达式。
4、eval() 虚拟机的入口,用于解释目标代码。虚拟机运行
void next() { // 当然了,现在只是读取每个字符,词法分析还没做
    token = *src++;
    return;
}
void program() {
    next();                  // get next token
    while (token > 0) {
        printf("token is: %c\n", token);
        next();
    }
}
因此,虚拟机就是一个eval
void eval() {
    int op, *tmp;
    while (1) {
        if (op == IMM)       {ax = *pc++;}                                     
// load immediate value to ax
        else if (op == LC)   {ax = *(char *)ax;}                               
// load character to ax, address in ax
        else if (op == LI)   {ax = *(int *)ax;}                                
// load integer to ax, address in ax
        else if (op == SC)   {ax = *(char *)*sp++ = ax;}                       
// save character to address, value in ax, address on stack
        else if (op == SI)   {*(int *)*sp++ = ax;}                             
// save integer to address, value in ax, address on stack
    }
    ...
    return 0;
}
注意虚拟机是永远循环的,如果不循环,应该是op等于什么。因为虚拟机里有指令EXIT
cycle ++;
op = *pc++; // get next operation code
赋值是最开始的这里进行的赋值。好了,差不多也全知道了。
op是什么并不知道,也没有赋值。。。
1、PC 程序计数器,它存放的是一个内存地址,该地址中存放着 下一条 要执行的计算机指令。
2、SP 指针寄存器,永远指向当前的栈顶。注意的是由于栈是位于高地址并向低地址增长的,所以入栈时 SP 的值减小。
3、BP 基址指针。也是用于指向栈的某些位置,在调用函数时会使用到它。
4、AX 通用寄存器,我们的虚拟机中,它用于存放一条指令执行后的结果。
虚拟机大致搞明白了,然后你写三地址码也写得出来,现在关键就是一个定义是如何转换成三地址码的呢?
如果转换成三地址码,它又是怎么样的呢?
match包含着next和容错。
void match(int tk) {
    if (token == tk) {
        next();
    } else {
        printf("%d: expected token: %d\n", line, tk);
        exit(-1);
    }
}
它将 next 函数包装起来,如果不是预期的标记则报错并退出。

发布了147 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/HeroIsUseless/article/details/104332237