没有AST, IDE中的错误提示自动补全重构语法检查......都玩不转了

Posted 码农翻身

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了没有AST, IDE中的错误提示自动补全重构语法检查......都玩不转了相关的知识,希望对你有一定的参考价值。

https://segmentfault.com/a/1190000017992387 

已经获得作者独家授权发布,老刘做了改编。 


张大胖一上班,领导就扔了一个任务给他,把项目中的javascript代码做点“小小”的改变: 


1. 把 == 改为全等 ===

2. 把parsetInt不标准的调用改为标准用法 parseInt(xxx)-> parseInt(xxx,10) 


对不熟悉JS的同学稍微解释一下: 


JS在比较两个变量的时候,双等号将执行类型转换; 三等号将进行相同的比较,而不进行类型转换 (如果类型不同, 只是总会返回 false ); 


parseInt(a,10) 表示以十进制的方式来解析。 


对于这些任务,张大胖脑海中马上闪现出了解决办法:字符串替换。 


对第一个任务: 找到'==',替换成'==='就行 。   


对第二个任务: parseInt(xxx) 改成parseInt(xxx,10), 没法直接替换,得写个正则表达式,找到那些只有一个参数的parseInt字符串,然后加上一个新的参数:10 。


张大胖对自己的正则表达式能力不太自信,如果考虑得不周全,代码就可能被改坏了。 


有没有别的办法? 


0 1
抽象语法树



使用正则表达式,只能把JavaScript源代码当做文本来处理,能力很弱,无法触及到JavaScript的语法层面,正则表达式没法知道这个地方是变量,那个地方是函数名.....  


如果能把JavaScript源码转化成结构化的对象,就可以精确地知道一段代码中有哪些变量名,函数名,参数......  这样就可以写程序就可以进行处理了。 


张大胖想起来自己没有考及格的《编译原理》,里边讲到了抽象语法树(AST)不就是所谓结构化的东西吗? 


比如表达式 result = 6+7*3 , 用抽象语法树来表示就是: 



如果把所有的JavaScript代码都转化成这样一颗AST的树,那代码的一切都尽在掌握, 可以任意修改了。 


没有AST, IDE中的错误提示、自动补全、重构、语法检查......都玩不转了



但是这其中有三个问题: 


1. 怎么从文本形式的源代码形成这么一个AST ?


让自己写程序实现那就太难了,得做词法分析,语法分析等等。  


2. 如何遍历这个AST,来修改这颗树的枝枝叶叶?    


比如我想在AST这棵树中添加一个新的节点,该怎么做?  


3. 修改完成以后,怎么再次把AST变成文本的源代码? 


张大胖赶紧打开Google 搜索,很快便找到了三个开源的工具,正好完成对应的三个功能: 


esprima : 从JavaScript源代码形成AST 


estraverse:遍历树的节点并修改 


escodegen : 把修改完的AST再次转化为源代码。 



0 2
创建AST



说干就干,张大胖准备了一段代码来做实验: 


//源码
function fun1(opt{
    if (opt.status == 1) {
        console.log('1');
    }
    if (opt.status == 2) {
        console.log('2');
    }
}
function fun2(age{
    if (parseInt(age) >= 18) {
        console.log('ok 你已经成年');
    }
}


使用esprima,轻轻松松就把它转化成了抽象语法树。 


//JS语法树模块
const esprima = require('esprima');
//创建AST
const AST = esprima.parseScript(jsCode);


 (由于转成树后结构非常大,这里不再展示了, 感兴趣的同学自己可以到http://esprima.org/demo/parse.html 去玩一把, 很有趣。 ) 


比如: if (parseInt(age) >= 18)  这一句,就被转化成了这样: 


没有AST, IDE中的错误提示、自动补全、重构、语法检查......都玩不转了



0 3
遍历修改AST



有了AST,就可以就是遍历和修改了,还是使用开源的工具。 


//JS语法树遍历各节点
const estraverse = require('estraverse');
//从JS语法树生成源代码
const escodegen = require('escodegen');

function walkIn(ast){
    estraverse.traverse(ast, {
        enter(node) => {
            toEqual(node);//把 == 改为全等 ===
            setParseInt(node); //parseInt(a)-> parseInt(a,10)
        }
    });
}


这个函数负责把‘==’改成‘===’


function toEqual(node{
    if (node.operator === '==') {
        node.operator = '===';
    }
}


这个函数负责把parseInt改成标准调用:  


function setParseInt(node{
    //判断节点类型 方法名称,方法的参数的数量,数量为1就增加第二个参数。
    if (node.type === 'CallExpression' && node.callee.name === 'parseInt' && node.arguments.length===1){
        node.arguments.push({//增加参数,其实就是数组操作
            "type""Literal",
            "value"10,
            "raw""10"
        });
    }
}


经过这个函数,原来的 if (parseInt(age) >= 18) 就变成了下图这样,相当于增加了一个节点,对应的代码就是 :if (parseInt(age,10) >= 18) 



最后使用escodegen 把修改过的AST再次变成源代码,就大功告成了: 


//生成目标代码
const code = escodegen.generate(ast);
//写入文件.....
//....你懂的


通过这个实验,张大胖基本上了解了AST的原理和用法,接下来可以着手正式的编程了。 


0 4
总结


本文的例子用AST也许不是最优解, 主要是为了展示AST的处理技术, AST实际上就是源代码的一种结构化表示, 利用它及相关工具可以方便地优化和修改代码,只要是你能对这棵“AST树”做“修剪”就可以对源代码做各种“手脚”: 


JavaScript代码语法、风格的检查 

在IDE中的错误提示、自动补全,重构 

代码的压缩和混淆 代码的转换 ......   


有这么强大的功能,AST处理技术是很多知名工具的基础, 例如babel,webpack,还有jd taro等都把AST用得淋漓尽致。 


你可能会 喜欢

码农翻身:用故事给技术加点料

以上是关于没有AST, IDE中的错误提示自动补全重构语法检查......都玩不转了的主要内容,如果未能解决你的问题,请参考以下文章

IDEA / WebStorm / PhpStorm 添加jQuery自动提示,自动补全,提醒文档

IDE选择

mac 下vscode 代码补全不提示,感觉好像是被选默认中了之后,就不提示的样子

在vscode中编写vue代码,new Vue还有其他vue语法都没有代码补全和提示,该怎么设置?

PCB 规则引擎之编辑器(语法着色,错误提示,代码格式化)

为啥我的Intellij Idea的代码补全中没有object