代码->抽象语法树的过程以及实现

137 阅读4分钟

背景

过程:代码 -> 词法分析 -> 构成抽象语法树 -> 运行抽象语法树

词法分析

sum = 1 * 23 + 4 * 5 如何将里面的词缀提取出来,一般是有限状态机
比如:

1.png

当我们在遍历字符串sum = 1 * 23 + 4 * 5时,一开始我们处于状态机的初始状态
当读取到s时,进入字符串状态
当读取到u时,保持在字符串状态
当读取到m时,保持在字符串状态
当读取到=时,将状态机的内容sum导出作为token,然后回归初始状态,然后进入操作符状态
当读取到1时,将状态机的内容=导出作为token,然后回归初始状态,然后进入数字状态
当读取到*时,token=1,进入操作符状态
当读取到2时,token=*,进入数字状态
当读取到3时,保持在数组状态
当读取到+时,token=23,进入操作符状态
当读取到4时,token=+,进入数字状态
当读取到*时,token=4,进入操作符状态
当读取到5时,token=*,进入数字状态
当读取到结束符时,token=5\

所以词缀获取:[sum=1*23+4*5]

问题:js的关键字和字符串状态似乎冲突了,比如let它该被解析成字符串还是关键字?
解决方法:给关键字赋予状态,比如let的状态,对于let sum = 1 * 23 + 4 * 5

0PAUSF7$P8L6~6TH5L@W8WA.png

在初始状态读取到l时,并不会进入字符串状态,而是let状态-l
继续读取到e时,进入let状态-le
继续读取到t时,进入let状态-let
如果继续读到字符的话,会进入字符串状态
这样就能实现关键字了。

逆波兰表达式,也叫后缀表达式

比如let sum = 1 * 23 + 4 * 5,操作符在操作数之间的表达方式是中缀表达式,而我们需要将它转化成后缀表达式:sum let 1 23 * 4 5 * + =
理论上,该表达式的操作符应该只有数字+-*/,但是我们会更加的广义,即需要有操作对象的符号都能被称作操作符,比如let需要一个变量名,那它就能被当做操作符,而这个变量名就是操作数
所以词缀集合[letsum=1*23+4*5]中的
操作符:let=*+*
操作数:sum12345

首先,我们需要一个栈放操作符,从左到右读取[letsum=1*23+4*5],使用rsl储存结果\

读取到let,是操作符,压入栈中
读取到sum,是操作数,rsl=sum,栈顶是单元操作符let,操作符出栈,rsl=sum let
读取到=, 是操作符,压入栈中
读取到1, 是操作数,rsl = sum let 1
读取到*, 是操作符,优先级比栈顶元素=高,*压入栈中
读取到23, 是操作数,rsl = sum let 1 23
读取到+, 是操作符,优先级比栈顶*低,栈顶*出栈,+压入栈中,rsl = sum let 1 23 *
读取到4, 是操作数,rsl = sum let 1 23 * 4
读取到*, 是操作符,优先级比栈顶+高,*压入栈中
读取到5, 是操作数,rsl = sum let 1 23 * 4 5
读取到结束符,栈内元素出栈,rsl = sum let 1 23 * 4 5 * + =

rsl就是后缀表达式

构造抽象语法树

对于后缀表达式:sum let 1 23 * 4 5 * + =
生成的抽象语法树如下:

{
    "type": "Program",
    "body": [{
        "type": "VariableDeclaration",
        "declarations": [{
            "type": "VariableDeclarator",
            "id": {
                "type": "Identifier",
                "name": "sum"
            },
            "init": {
                "type": "BinaryExpression",
                "left": {
                    "type": "BinaryExpression",
                    "left": {
                        "type": "Literal",
                        "value": 1,
                        "raw": "1"
                    },
                    "operator": "*",
                    "right": {
                        "type": "Literal",
                        "value": 23,
                        "raw": "23"
                    }
                },
                "operator": "+",
                "right": {
                    "type": "BinaryExpression",
                    "left": {
                        "type": "Literal",
                        "value": 4,
                        "raw": "4"
                    },
                    "operator": "*",
                    "right": {
                        "type": "Literal",
                        "value": 5,
                        "raw": "5"
                    }
                }
            }
        }],
        "kind": "let"
    }],
    "sourceType": "module"
}

生成过程:从右往左读取
读取=,是双元操作符
读取+,是双元操作符,其本身作为=的右操作数
读取*,是双元操作符,其本身作为+的右操作数
读取5,是字面量,其本身作为*的右操作数
读取4,是字面量,其本身作为*的做操作数
读取*,是双元操作符,其本身作为+的左操作数
读取23,是字面量,其本身作为+的右操作数
读取1,是字面量,其本身作为+的左操作数
读取let,是关键字,也是单元操作符,其本身作为=的左操作数
读取sum,是字符串,其本身作为let的操作数

PS

字典树是一种很好的实现字符串型有限状态机的数据结构