forked from DMQ/js-parser
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ast-parser.js
281 lines (218 loc) · 6.66 KB
/
ast-parser.js
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
const { TokenStream } = require('./token-stream');
const { parserTools, tokenTools } = require('./tools');
const { tokenTypes, astTypes } = require('./types');
class Parser {
constructor(tokenStream) {
this.tokenStream = tokenStream instanceof TokenStream ? tokenStream : new TokenStream(tokenStream);
}
skipPunctuator(punctuator) {
if (parserTools.isPunctuator(this.tokenStream.peek(), punctuator)) {
this.tokenStream.next();
} else {
this.tokenStream.error(`异常的符号: ${punctuator}`);
}
}
skipKeyword(keyword) {
if (parserTools.isKeyword(this.tokenStream.peek(), keyword)) {
this.tokenStream.next();
} else {
this.tokenStream.error(`异常的关键字: ${keyword}`);
}
}
unexpected() {
this.tokenStream(`意外的token: ${JSON.stringify(this.tokenStream.peek())}`);
}
parseIdentifier() {
const token = this.tokenStream.peek();
// TODO: 可以对token做类型判断,抛出异常
var node = {
type: astTypes.Identifier,
name: token.value,
};
this.tokenStream.next();
return node;
}
parseLiteral() {
const token = this.tokenStream.peek();
var node = {
type: parserTools.isNumber(token) ? astTypes.NumericLiteral : astTypes.StringLiteral,
value: token.value,
};
this.tokenStream.next();
return node;
}
parseVariable() {
const token = this.tokenStream.next(); // 拿出var
// 变量声明
const variableDeclaration = {
type: astTypes.VariableDeclaration,
declarations: [], // 可能存在多个变量声明
kind: token.value,
};
// TODO: 处理前可以做一些异常处理
// 遍历token,遇到分号结束(换行先不考虑),var a = 1; var a,b = 1;均满足
while(!this.tokenStream.eof()) {
const node = {
type: astTypes.VariableDeclarator,
id: this.parseIdentifier(), // 解析标识符'var'
init: null, // 初始赋值,多个变量声明的情况,只有最后一个节点赋初始值(estree规范)
};
// 将单个变量声明节点,push进去
variableDeclaration.declarations.push(node);
// 1、多个变量声明的情况,跳过逗号,进入下一轮循环继续遍历其他变量
if (parserTools.isPunctuator(this.tokenStream.peek(), ',')) {
this.tokenStream.next();
continue;
}
// 2、判断是否=号赋值
if (parserTools.isOperator(this.tokenStream.peek(), '=')) {
this.tokenStream.next();
node.init = this.parseStatement(); // 初始值,可能是字面量,可能是表达式,递归调用即可
}
break; // 跳出循环
}
this.skipPunctuator(';'); // 跳过分号(非分号结尾此处会报错,暂不考虑换行)
return variableDeclaration;
}
parseParams() {
const params = [];
this.skipPunctuator('(');
while (!this.tokenStream.eof()) {
// 括号闭合,结束
if (parserTools.isPunctuator(this.tokenStream.peek(), ')')) {
break;
}
// 标识符作为参数
params.push(this.parseStatement());
// 跳过逗号(如果有多个参数的场景)
if (parserTools.isPunctuator(this.tokenStream.peek(), ',')) {
this.tokenStream.next();
}
}
this.skipPunctuator(')');
return params;
}
parseBlock() {
this.skipPunctuator('{');
const node = {
type: astTypes.BlockStatement,
body: [],
};
// 遇到闭合大括号之前,循环解析语句
while(!parserTools.isPunctuator(this.tokenStream.peek(), '}')) {
node.body.push(this.parseStatement()); // 递归遍历即可
}
this.skipPunctuator('}');
return node;
}
parseFunction() {
this.skipKeyword('function');
const node = {
type: astTypes.FunctionDeclaration,
id: this.parseIdentifier(),
};
node.params = this.parseParams();
node.body = this.parseBlock();
return node;
}
// 赋值表达式
parseAssignExpression(left, operator) {
return {
type: astTypes.AssignmentExpression,
left,
operator,
right: this.parseStatement(),
};
}
// 表达式语句
parseExpressionStatement(expr) {
const node = {
type: astTypes.ExpressionStatement,
expression: expr,
};
this.skipPunctuator(';'); // 分号结尾
return node;
}
parseMemberExpression(id) {
this.skipPunctuator('.');
const node = {
type: astTypes.MemberExpression,
object: id,
property: this.parseIdentifier(),
};
return node;
}
// 解析可能存在的二元运算
parseMaybeBinary(left) {
const token = this.tokenStream.peek();
// 赋值操作
if (parserTools.isOperator(token, '=')) {
this.tokenStream.next(); // 跳过等号
const expr = this.parseAssignExpression(left, '=');
return this.parseExpressionStatement(expr);
}
return left;
}
// 解析可能存在的调用
parseMaybeCall(left) {
// return left;
const token = this.tokenStream.peek();
if (parserTools.isPunctuator(token, '(')) {
let node = {
type: astTypes.CallExpression,
callee: left,
arguments: this.parseParams(),
};
return this.parseExpressionStatement(node);
}
return left;
}
// 解析语句
parseStatement() {
const token = this.tokenStream.peek();
// 变量声明
if (parserTools.isKeyword(token, 'var')) {
return this.parseVariable();
}
// 函数声明
if (parserTools.isKeyword(token, 'function')) {
return this.parseFunction();
}
return this.parseExpression();
}
// 解析表达式
parseExpression() {
const token = this.tokenStream.peek();
let left;
// 变量(可能存在二元运算、函数调用)
if (parserTools.isIdentifier(token)) {
left = this.parseIdentifier();
// 成员表达式,不考虑obj.a.b和obj['a']等场景
if (parserTools.isPunctuator(this.tokenStream.peek(), '.')) {
left = this.parseMemberExpression(left);
}
}
// 字面量
if (parserTools.isString(token) || parserTools.isNumber(token)) {
// 字符串、数字字面量(可能存在二元运算)
left = this.parseLiteral();
}
if (!left) {
return this.tokenStream.next(); // 避免解析不完整,导致死循环,直接将token输出
}
return this.parseMaybeCall(this.parseMaybeBinary(left));
}
// 解析入口
parse() {
const program = {
type: astTypes.Program,
body: [],
};
// 逐步解析
while(!this.tokenStream.eof()) {
program.body.push(this.parseStatement());
}
return program;
}
}
module.exports = Parser;