前言
一直忙于工作,好久没写blog了
今天上来把这篇(也没写完其实)更新一下表示还活着吧
最近在做的一个技术项目,不过最后也没用acorn用了@babel/parser(其实也是基于acorn)
然后看了下acorn的源码,挺长的,几千行= =
不过也不难理解
acorn介绍
A tiny, fast JavaScript parser written in JavaScript.
acorn是一个js解析器,可以将js文件转成想要的ast用于进一步分析。
可以搭配esquery对ast进行提取。
@babel/parser便是基于acorn进一步封装的,对于jsx文件、class代码有较好的解析性能。
acorn的基本使用方法如下:
const acorn = require('acorn');
const node = acorn.parse(content, options);
- content为文件内容,可以通过fs.readFileSync(filePath)获取。
- options为解析的配置信息,包括 ecma 版本、 sourceType (可选 script 或者 module ,选择 module 时文件内的 import/exports 将能被正常解析,即使ecma版本小于6)、 allowHashBang (能正常解析#!开头的文件,对于#!会被视为注释)等。
很明显可以看出,入口是acorn的parse函数,接下来也将从parse开始一层层往下走。
从入口:parse()开始
parse 是acorn解析的入口,parse函数接受两个参数,在源码里是这样的:
const parse = (input, options) => Parser.parse(input, options);
对外暴露的parse函数实际上指向了Parser的parse函数。
那么Parser是什么呢,Parser是一个全局对象,上面挂载了大量的与解析相关的函数,其中当然也包括parse函数。
Parser对象大概可以如此简短的描述下:
var Parser = function Parser(options, input, startPos) {
this.options = options;
this.input = input;
if (startPos) {
this.pos = startPos;
}
};
其中还包含了一堆与options有关的属性,这里暂且不介绍了。 这里只是介绍一下Parser大概是啥样的,重点是Parser.parse函数,作为实际接受数据和配置的入口函数,其实代码很短很清晰:
Parser.prototype.parse = function parse() {
var node = this.options.program || this.startNode(); // 获取当前ast节点或者从一个空的ast节点开始
this.nextToken(); // 更新当前token
return this.parseTopLevel(node); // 从顶部开始解析,递归下降地组装成ast
};
代码总共就三行,也没啥好说的:
- 获取当前ast节点,如果没有就新建一个(this.startNode()的功能是返回一个空的ast节点)
- 更新当前的token
- 从顶部开始解析,递归下降地组装成ast
parse只是一个入口,在这里我们先不看parseTopLevel(这也只是一个入口,里面还有一堆函数),我们先从第二行nextToken(这也算是一个入口)开始——acorn的词法分析过程。