构建自己的编译器(七)词法分析之二

对于标识符,已经存在的那些,有初始的一张表,然后是自己定义的变量名函数名什么的,也要存入一张表中,如果标识符存在就返回该标识符,实际上,词法分析后,有很大一部分都变成了id了。

代码上int *current_id这样的

int *current_id, // current parsed ID
    *symbols,    // symbol table
    line,        // line number of source code
    token_val;   // value of current token (mainly for number)
就是正在解析的。。。
传统意义上的符号表只需要知道标识符的唯一标识即可,而我们还存放了一些只有语法分析器才会得到的信息,如 type 。

由于我们的目标是能自举,而我们定义的语法不支持 struct,故而使用下列方式。

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

即用一个整型数组来保存相关的ID信息。
也就是说,它是为了实现自我编译才弄成这么别扭的样子。
那么几乎什么都是int *了。

// fields of identifier

enum {Token, Hash, Name, Type, Class, Value, BType, BClass, BValue, IdSize};

那么,next函数的主要功能就是弄出一个current_id吧就是。不,是弄出一个token,标识符是标识符,它是细化的一部分。如果token是一个标识符,那么它就找currentid的

那么变量定义还是没说。

枚举类型也可以定义为全局常量。

match(';'); 你看,就是这么个东西,它可以检查错误。同时可以向下进行。
找到了
 

// variable declaration

            current_id[Class] = Glo; 

// global variable

            current_id[Value] = (int)data; 

// assign memory address

            data = data + sizeof(int);

        }
关键在于这里,data就是数据段,data往后移走了sizeof,也就是4个字节。data是char类型的,为什么?因为是上面current_id[Value] = (int)data; 啥意思,就是现有id的value的地方被赋予了data的地址,因为是char*类型的,就是说给这个变量分配了这个地址呗,然后这个变量进入了一个变量表,查询的时候是线性查询。把这个变量存入了变量表。也就是说,定义一个内存表来存放变量的。。。大概也就明白了。

那么说,如果我这个虚拟机不符合x86子集,那么我会更方便的编写。实话说,它这里的虚拟机并没有实现定义变量的功能。你说都变成三地址码然后传进去吧,你就要实现三地址码中定义变量,然后虚拟机自己再填入。。。三地址码有缺陷,它少data段的定义。一个闭合的虚拟机真的很难弄,这数据段你弄好了,人家还得解析。但是说实话,一个汇编器着实很好弄。

因此,还是说,一个非x86格式的虚拟机好弄。

而且用不到封装。当然了,你设计的中间代码不好理解,但是方便在虚拟机上跑,就是这样。但是是一个非常完美的结构,就用这个结构进行求解。分为顺序语句,条件语句,循环语句。每个语句都有自己的结构,当然了,嵌套构,都是顺序结构的嵌套结构。最后解析出来的就是一个嵌套结构。还是说,语句呢?定义语句,赋值语句,我觉得这个很容易反汇编成C语言了。反正我不管,我就要虚拟机能运行就行。其实都是一条条顺序语句,通过jmp来定位不同的位置而已,而定于语句,赋值语句,先别管重复定义 问题,都是用一条语句结构sentence,定义sentence和赋值sentence是一样的,对变量表进行操作,分支语句和循环语句呢,就是对jmp的操作,我们先不管那么多,毕竟机器就是这样执行的。

那么print函数呢?首先识别出来它是一条函数语句,不行,现在你就像重新设计架构一样,表达式的确是相对独立的,现阶段不考虑表达式,你能够分析的出来那是一个表达式,至于表达式究竟插在哪里,那么就是明天看了,明天看看函数,表达式,就这样。

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

猜你喜欢

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