【概述】
解析代码分为四个步骤:第一步,初步读取并存为二维字符数组;第二步,解读二维数组,将其中的信息进行提取进节点并组成一条链表,并且将每段代码的作用域按照体度补全(比如当遇到第五层局部变量的下一行直接变成第三层局部变量时,将添加空指令节点其修改为第五层到第四层,第四层到第三层这样的);第三步,初步检查语法,要求指定关键字之后必须进入新的一层局部变量;第四步,补全跳行逻辑。
(资料图片仅供参考)
【第一步】
直接创建一个初始化为0的container二维字符数组,然后将文本中的每一行读取放进这个二维数组,然后创建一个cmd结构体作为命令起始点,并且将其初始化,之后所以的操作都会以此开端进行遍历,这一步都在main函数中完成。
【第二步】
虽然程序已经进入cmdparse函数,但实现部分在其中的cmdread函数中。
简单来讲cmdread函数有两大用处。首先,函数的输入为上一条指令的地址、字符串形式的命令、作用域,然后用malloc动态地为本行指令在堆中分配内存方便后后面的操作,如果遇到scope减少就行对应的进行wrap up,具体内容见图。
【第三步】
对初步解析好的链表进行遍历,检查在特定关键字后面是否如要求进行缩进,以表示进入新的局部变量范围,这一步比较简单没写注释。
【第四步】
对于跳行的解析,也是这一章最抽象最难的一部分。在Felys中,自动解析的跳行有三种:
当while、if、elif的判断结果为否的时候,我们需要跳过当中全部的指令直接执行靠近其作用域最近的一条,比如当前while的scope为0且while判定为否,那我们就需要跳过当中所有scope大于0的命令,那么目标就是将while之后第一条scope为0的cmd的地址写到while的jump里面。由于编程语言都遵循层层相扣的原理,这里完美契合栈的特性,每当遇到这三者时,我们就压入栈内,当遇到其scope结束的时候就把它弹出来,并且修改其jump的跳转位置。在运行的时候,假如while的判断是TRUE那么执行下一行,否则执行跳行,if和elif同理。
while的结束前的最后一行应该直接jump回while进行判断,所以这里的目标就是将while循环中最后一行cmd的jump写入对应的while的cmd地址,这样执行到这里的时候就会自动跳转到while的判断(注:如果一条cmd在没有关键字的情况下,jump却不是NULL,那就直接执行跳行而不是下一行,这部分会在执行章节详解)。
如果if和elif在执行完成之后需要跳过后面所有的elif和else,实现原理是遇到if或者elif的时候我们就压栈,当遇到一条scope等于if或者elif的cmd且其关键字不等于elif和else的话,就清空栈中直到栈顶cmd的scope不再等于当前cmd的scope。具体内容见图。
说实话,我当时花了整整一周时间才算得到一个稳定有逻辑操作方式,尽管我在此之前已经完全构思好了大体逻辑和期望结果,每次写完一版之后都会遇到重大漏洞决定重构,因为不仅非常抽象而且对细节要求很高,但我这个人比较执着,执意要用符合逻辑的算法来写而不是愚蠢的疯狂遍历(时间复杂度会直接起飞),最终得到了这个O(2n)的版本(语法检查那个遍历不算),我还是很满意的。
【总结】
如果你基本理解了解析遍,那么恭喜你对Felys整体构造已经有了相当的了解,暂时看不懂也没关系,因为每一章节都是可以独立阅读的,可以先看看其他的回过头来再理解整体架构。
[责任编辑:linlin]
标签: