本文最后更新于:a few seconds ago
前言
javascript
是解释型语言,在v8中,源代码需要经过词法和语法分析,转化为AST, 再进行语义分析,最后转化成字节码执行。
语法分析成AST可以在这里体验:https://esprima.org/demo/parse.html。
本次deno 实现的源码在这里获取:deno-ast,开源学习随意复制。
Parser(解析器)
Parser分为Tokenizer(分词器),和parser(解析器)。
Tokenizer
分词器的最终目的是帮助我们将代码进行词法分析,分割成一个一个的小token,方便我们的parser去解析成AST.
我们先来测试一下我们tokenizer
的效果。
把源码clone下来之后,运行:
| deno test --filter "tokenizerTest"
|
这些就是我们生产的token,有了这些token,我们的parser才能转化为AST。
tokenizer
最核心的就是正则表达式,本质上就是用正则表达式去提取token。具体的正则定义可以查看Tokenizer.ts文件。
Parser
接下来继续看parser,里面主要的内容就是下面两个
- BNF(巴科斯范式)
- Finite-state machine(有限状态机)
巴科斯范式
巴科斯范式我个人的理解是一种使用递归思想来描述计算机语言的定义规范。
想进一步学习可以查看bnf等相关内容。
不过,个人觉得最值得学习的还是编译器的优化,这比较重要。
我们来看一个代码的结构声明。代码位于parser
|
ReturnStatement() { this._eat('return'); const argument = this.currentToken.type !== ';' ? this.Expression() : null; this._eat(';');
return { type: 'ReturnStatement', argument, }; }
|
上面就是对return的一个声明,表示这个ReturnStatement
需要以下结构:
: ‘return’ OptExpression ‘;’
一个return关键字,加上可选择的表达(Expression在代码下面有进行结构声明),然后结尾一个分号表示结束。
而一些Statement
状态的转移主要是依赖我们的Statement函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
Statement(): Record<string, unknown> | undefined { switch (this.currentToken.type) { case ";": return this.EmptyStatement(); case "if": return this.IfStatement(); case "{": return this.BlockStatement(); case "let": return this.VariableStatement(); case "fun": return this.FunctionDeclaration(); case "class": return this.ClassDeclaration(); case "return": return this.ReturnStatement(); case "while": case "do": case "for": return this.IterationStatement(); default: return this.ExpressionStatement(); } }
|
他帮助我们把tokeninzer
生成的token转移到各个具体的声明去,构造我们的抽象语法树。
Finite-state machine(有限状态机)
在我们的parser里,可以理解成一系列状态的转移,每个方法都是一个状态。
每个函数的调用可以看作是一个状态的转移。
比如parser.ts
里状态的转移是:
Program -> StatementList -> Statement …
最终的状态都会在Literal
结束。
输出AST
我们运行以下测试,就能得到我们要的结果:
| deno test --filter "parserTest"
|
这样我们就将代码转换为抽象语法树了。
| class add { fun constructor(x, y) { this.x = x; this.y = y; }
fun calc() { return this.x + this.y; } }
|
AST
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| { "type": "Program", "body": [ { "type": "ClassDeclaration", "id": { "type": "Identifier", "name": "Point" }, "superClass": null, "body": { "type": "BlockStatement", "body": [ { "type": "FunctionDeclaration", "name": { "type": "Identifier", "name": "constructor" }, "params": [ { "type": "Identifier", "name": "x" }, { "type": "Identifier", "name": "y" } ], "body": { "type": "BlockStatement", "body": [ { "type": "ExpressionStatement", "expression": { "type": "AssignmentExpression", "operator": "=", "left": { "type": "MemberExpression", "computed": false, "object": { "type": "ThisExpression" }, "property": { "type": "Identifier", "name": "x" } }, "right": { "type": "Identifier", "name": "x" } } }, { "type": "ExpressionStatement", "expression": { "type": "AssignmentExpression", "operator": "=", "left": { "type": "MemberExpression", "computed": false, "object": { "type": "ThisExpression" }, "property": { "type": "Identifier", "name": "y" } }, "right": { "type": "Identifier", "name": "y" } } } ] } }, { "type": "FunctionDeclaration", "name": { "type": "Identifier", "name": "calc" }, "params": [], "body": { "type": "BlockStatement", "body": [ { "type": "ReturnStatement", "argument": { "type": "BinaryExpression", "operator": "+", "left": { "type": "MemberExpression", "computed": false, "object": { "type": "ThisExpression" }, "property": { "type": "Identifier", "name": "x" } }, "right": { "type": "MemberExpression", "computed": false, "object": { "type": "ThisExpression" }, "property": { "type": "Identifier", "name": "y" } } } } ] } } ] } } ] }
|
我们可以看到,最外层包裹着Program
,而class 的类型为ClassDeclaration
。
而他的body里又包裹着两个FunctionDeclaration
,也就是我们的函数声明fun
。
结束语
分享一个最近很喜欢的表情包哈哈
感谢