使用acorn对JavaScript代码进行解析。

来源:互联网 发布:淘宝夜间模式 编辑:程序博客网 时间:2024/06/08 02:41

这里写图片描述

使用acorn对JavaScript代码进行解析。
Acorn简介

Acorn是一个小小的快速JavaScript解析器,完全用JavaScript编写并且性能和效率比Esprima更胜一筹。解析思路是把代码解析成语法树的形式。如下代码是一个简单的例子。

c = 1 + 1;

通过Acorn解析结果如下:
这里写图片描述

AST树简介

在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax
tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于if-condition-then这样的条件跳转语句,可以使用带有两个分支的节点来表示。
AST树作为程序的一种中间表示形式,在程序分析等诸多领域有广泛的应用.利用抽象语法树可以方便地实现多种源程序处理工具,比如源程序浏览器、智能编辑器、语言翻译器等.

var AST = "is Tree";

这里写图片描述

这里的解析结果和Acorn的解析结果看起来很像,他们在形式上是相同的区别只是在于对不同信息的保存上或多或少,比如下代码

int a = 1;//给a赋值int b = 2;

Acorn的处理逻辑会把注释和第二句代码链接起来,而Esprima则是会注释的信息分别和两句话都链接起来。那么这也就在处理代码和注释的对应上有一点误差(后面会讲到)。
解析过程和核心代码介绍
要使用Acorn进行解析那么第一步就是安装Acorn,通过npm install acorn 即可安装。

我们的目的是提取JavaScript代码中的注释和对应的代码,所以对应的操作就是
1. 如何解析出AST树。
2. 如何遍历AST树拿出其中的注释和找到注释对应的代码。
3. 如何把注释和注释对应的代码存成对应的格式。

Acorn对代码的解析很简单

var ast = acorn.parse(source, {            sourceType: "script",            locations: true,            onComment: comments,            onToken: tokens,        });

官方给出这样的解释“*parse(input, options) is used to parse a JavaScript
program. The input parameter is a string, options can be undefined or
an object setting some of the options listed below. The return value
will be an abstract syntax tree object as specified by the ESTree
spec.*”通过以上操作我们便可以得到解析完成AST,第一个参数是String类型的文本,第二个参数是一个对象,是对AST树的一些设置。下边对上面用到的参数做一个收集

locations :When true, each node has a loc object attached with start and end subobjects, each of which contains the one-based line and zero-based column numbers in {line, column} form. Default is false.
onComment: If a function is passed for this option, whenever a comment is encountered the function will be called with the following parameters:

block: true if the comment is a block comment, false if it is a line comment.text: The content of the comment.start: Character offset of the start of the comment.end: Character offset of the end of the comment.

每个注释都会被按照下面的形式存储起来
这里写图片描述
当我们通过Acorn拿到了带有完整注释信息的AST树那么就有对树进行一个遍历,取出对注释和注释对应的代码进行存储。

//注释说明1int a = 2;//注释说明2int b = 3;//注释说明3

经过对于AST树中的节点来说 注释一是代码一的leadingComments,注释而是代码一的trailingComments,同样代码二也有对应的leadingComments和trailingComments分别是注释二和注释三。但是也有一种特殊的情况如下

int a = 2; //注释说明1int b = 3;//注释说明2

注释一和注释二会被认为是代码二的leadingComments和trailingComments,而代码一没有trailingComments。这样的话就和我们的处理逻辑是不一样的,我们需要然后上面例子中注释一是代码一的trailingComments。所以就需要对整个树进行一个遍历,对节点信息记性一个设置。
遍历会用到estraverse这个库。处理逻辑如下代码所示

estraverse.traverse(ast, {    leave: function (node, parent) {         if (!node.hasOwnProperty('trailingComments')) {             var old_node = node;             var old_line = node.loc.end.line;             var old_column = node.loc.end.column;             estraverse.traverse(parent, {              leave: function (node, parent) {              if (node !== old_node &&              node.hasOwnProperty('leadingComments') &&              node.leadingComments[0].loc.start.line === old_line &&              node.leadingComments[0].loc.start.column >= old_column) {              old_node.trailingComments = [];              old_node.trailingComments[0] = node.leadingComments.shift();              if (node.leadingComments.length === 0) {                     delete node.leadingComments;                     }                     estraverse.VisitorOption.Break;                  }               }          });              node = old_node;   }

通过如上操作便可对上文提到的特殊情况进行更正。
现在最后一个问题就是如何把得到的注释和代码对应起来,对AST树来说这里操作就很简单了,我们需要对AST在遍历时候对每个节点的状态进行判别。

var t = node.hasOwnProperty('trailingComments');var l = node.hasOwnProperty('leadingComments');if (t && l) {    comment_code.comment = node.leadingComments.concat(node.trailingComments);    comment_code.code.push(escodegen.generate(node));//将注释和代码对应起来    extract.push(comment_code);    }else if (l) {comment_code.comment = node.leadingComments;comment_code.code.push(escodegen.generate(node));extract.push(comment_code);    }else if (t) {    comment_code.comment = node.trailingComments;    comment_code.code.push(escodegen.generate(node));    extract.push(comment_code);    }comment_code = {comment: [], code: []};

把代码和注释都存在数组中之后,通过

var fs = require("fs");fs.writeFileSync(filepaht, filecontent);

进行存储。到此处理完毕。

0 0
原创粉丝点击