樹是一種重要的數據結構,由根結點和若干顆子樹構成的。 根據結構的不一樣又能夠劃分爲二叉樹,trie樹,紅黑樹等等。
今天研究的對象是AST,抽象語法樹,它以樹狀的形式表現編程語言的語法結構,樹上的每一個節點都表示源代碼中的一種結構。
經過操做這棵樹,能夠精準的定位到聲明、賦值、運算語句,從而實現對代碼的分析、優化、變動等操做。webpack
- 代碼風格,語法的檢查,IDE中的錯誤提示,格式化,自動補全等等
- 優化變動代碼,代碼壓縮等等
- es6轉es5,以及TypeScript、JSX等轉化爲原生Javascript等等
js中藉助於一些庫能夠把js源碼解析爲語法樹,好比 Babylon, esprima、acorn、UglifyJS、AST explorer等等,以下所示是一個簡單的示例。git
var a = 42; var b = 5; ar c = a + b;
說明 一個簡單的ast樹示例,對應的json格式以下所示es6
{ "type": "Program", "start": 0, "end": 37, "body": [ { "type": "VariableDeclaration", "start": 0, "end": 11, "declarations": [ { "type": "VariableDeclarator", "start": 4, "end": 10, "id": { "type": "Identifier", "start": 4, "end": 5, "name": "a" }, "init": { "type": "Literal", "start": 8, "end": 10, "value": 42, "raw": "42" } } ], "kind": "var" }, { "type": "VariableDeclaration", "start": 12, "end": 22, "declarations": [ { "type": "VariableDeclarator", "start": 16, "end": 21, "id": { "type": "Identifier", "start": 16, "end": 17, "name": "b" }, "init": { "type": "Literal", "start": 20, "end": 21, "value": 5, "raw": "5" } } ], "kind": "var" }, { "type": "VariableDeclaration", "start": 23, "end": 37, "declarations": [ { "type": "VariableDeclarator", "start": 27, "end": 36, "id": { "type": "Identifier", "start": 27, "end": 28, "name": "c" }, "init": { "type": "BinaryExpression", "start": 31, "end": 36, "left": { "type": "Identifier", "start": 31, "end": 32, "name": "a" }, "operator": "+", "right": { "type": "Identifier", "start": 35, "end": 36, "name": "b" } } } ], "kind": "var" } ], }
經過操縱解析出來的ast,能夠實現咱們AST應用場景中列出的一些應用。
下面針對上面列出的ast樹作一些簡單說明:
任何一顆ast樹根節點的類型都是Program,start和end記錄了字符的位置,body表示程序體,其內部是三個簡單的變量聲明,每一個變量聲明中記錄了標示符以及字面量的值。最後一個變量c中init是一個BinaryExpression(二元運算表達),記錄的不是字面值,而是引用到的標示符和操做符。想要實現應用場景中舉的示例,大體就是遍歷,修改,刪除,移動這棵樹上的節點,最後遍歷處理後ast生成最終代碼。github
//compile.js this.hooks.make.callAsync(compilation, err => {}); ---- //NormalModules.js runLoaders( { resource: this.resource, loaders: this.loaders, context: loaderContext, readResource: fs.readFile.bind(fs) }, (err, result) => { this._source = this.createSource( this.binary ? asBuffer(source) : asString(source), resourceBuffer, sourceMap ); return callback(); } ); ---------------------------------- //Parse.js const acorn = require("acorn-dynamic-import").default; ast = acorn.parse(code, parserOptions); if (this.hooks.program.call(ast, comments) === undefined) { this.detectStrictMode(ast.body); this.prewalkStatements(ast.body); this.walkStatements(ast.body); }
說明 上面是webpack源碼中摘取的和ast處理有關的上下文關鍵片斷
在webpack執行流程中,make是一個重要的階段,在一個新的 Compilation 建立完畢後,即將從 Entry 開始讀取文件,根據文件類型和配置的 Loader 對文件進行編譯,將loader處理後的文件經過acorn抽象成抽象語法樹AST,而後遍歷AST,遞歸分析構建該模塊的全部依賴。web
發現ast水很深,平時接觸的也比較少, 今天算是個入門瞭解下,做爲理解webpack源碼前的鋪墊。
參考源碼
webpack: "4.4.1"
webpack-cli: "2.0.13"
參考文檔
https://github.com/acornjs/acorn
https://zh.wikipedia.org/wiki...
https://www.sitepoint.com/und...編程