沒有AST, IDE中的錯誤提示、自動補全、重構、語法檢查

01

抽象語法樹

使用正則表達式,只能把JavaScript源代碼當做文本來處理,能力很弱,無法觸及到JavaScript的語法層面,正則表達式沒法知道這個地方是變量,那個地方是函數名.....

如果能把JavaScript源碼轉化成結構化的對象,就可以精確地知道一段代碼中有哪些變量名,函數名,參數...... 這樣就可以寫程序就可以進行處理了。

張大胖想起來自己沒有考及格的《編譯原理》,裡邊講到了抽象語法樹(AST)不就是所謂結構化的東西嗎?

比如表達式 result = 6+7*3 , 用抽象語法樹來表示就是:

沒有AST, IDE中的錯誤提示、自動補全、重構、語法檢查


如果把所有的JavaScript代碼都轉化成這樣一顆AST的樹,那代碼的一切都盡在掌握, 可以任意修改了。

沒有AST, IDE中的錯誤提示、自動補全、重構、語法檢查


但是這其中有三個問題:

1. 怎麼從文本形式的源代碼形成這麼一個AST ?

讓自己寫程序實現那就太難了,得做詞法分析,語法分析等等。

2. 如何遍歷這個AST,來修改這顆樹的枝枝葉葉?

比如我想在AST這棵樹中添加一個新的節點,該怎麼做?

3. 修改完成以後,怎麼再次把AST變成文本的源代碼?

張大胖趕緊打開Google 搜索,很快便找到了三個開源的工具,正好完成對應的三個功能:

esprima

: 從JavaScript源代碼形成AST

estraverse:遍歷樹的節點並修改

escodegen : 把修改完的AST再次轉化為源代碼。

02

創建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中的錯誤提示、自動補全、重構、語法檢查


03

遍歷修改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)

沒有AST, IDE中的錯誤提示、自動補全、重構、語法檢查


最後使用escodegen 把修改過的AST再次變成源代碼,就大功告成了:

//生成目標代碼

const code = escodegen.generate(ast);

//寫入文件.....

//....你懂的

通過這個實驗,張大胖基本上了解了AST的原理和用法,接下來可以著手正式的編程了。

04

總結

本文的例子用AST也許不是最優解, 主要是為了展示AST的處理技術, AST實際上就是源代碼的一種結構化表示, 利用它及相關工具可以方便地優化和修改代碼,只要是你能對這棵“AST樹”做“修剪”就可以對源代碼做各種“手腳”:

JavaScript代碼語法、風格的檢查

在IDE中的錯誤提示、自動補全,重構

代碼的壓縮和混淆 代碼的轉換 ......



分享到:


相關文章: