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

使用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);

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: acorn 中的 postfix 和 prefix 指的是代码表达式的求值方式。 - postfix(后缀表达式):运算符在两个操作数之后,如 "3 4 + 5 ×"。 - prefix(前缀表达式):运算符在两个操作数之前,如 "× 5 + 4 3"。 在 acorn 中,这些术语是用来描述 JavaScript 表达式的语法树是如何构建的,以及如何从语法树中求值。 ### 回答2: 在acorn中,postfix和prefix分别是指在函数调用时的不同位置。 1. Postfix(后缀):在函数名之后的参数列表称为后缀。举个例子,考虑以下acorn代码片段: ```javascript console.log(10.toExponential(2)); ``` 在这个例子中,函数调用toExponential(2)被放在了后缀位置。这意味着,我们首先指定了一个数字10,然后使用点号操作符调用了toExponential函数,并将2作为参数传递给该函数。 2. Prefix(前缀):在函数名之前的参数列表称为前缀。举个例子,考虑以下acorn代码片段: ```javascript console.log(Math.floor(3.14)); ``` 在这个例子中,函数调用Math.floor(3.14)被放在了前缀位置。这意味着,我们首先指定了函数名Math.floor,并使用括号将参数3.14包裹起来,作为该函数的参数。 总结起来,postfix和prefix在acorn中主要用于标识函数调用时参数列表的位置。通过清楚地理解这两个概念,我们可以正确地使用函数和参数进行编程。 ### 回答3: 在acorn中,postfix和prefix是指一元运算符的两种形式。 Prefix是指运算符位于其操作数之前的形式。例如,"+ 2 3"中的"+"是一个prefix运算符,它在其两个操作数2和3之前。这意味着它将2和3相加,返回结果5。 Postfix是指运算符位于其操作数之后的形式。例如,"2 3 +"中的"+"是一个postfix运算符,它在其两个操作数2和3之后。这意味着它将2和3相加,返回结果5。 在acorn中,prefix和postfix形式的一元运算符可以用于各种数学计算,包括算术运算(例如+,-,*,/)和其他数学运算(例如sin,cos,sqrt等)。 运算符的选择与操作数的顺序有关。在prefix形式中,运算符出现在操作数之前,而在postfix形式中,运算符出现在操作数之后。这两种形式在编程语言中都有不同的应用和使用场景,可以根据具体需求和语言规范选择使用其中之一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值