您的位置:首页 > Web前端 > JavaScript

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

2017-04-15 16:08 561 查看


使用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树那么就有对树进行一个遍历,取出对注释和注释对应的代码进行存储。

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


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

int a = 2; //注释说明1
int 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);


进行存储。到此处理完毕。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript Acorn解析