Acorn源码分析(1)

Posted by zhykirby on November 12, 2020

前言

一直忙于工作,好久没写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的词法分析过程。