前言javascript
首先,先說明下該文章是譯文,原文出自《AST for JavaScript developers》(https://itnext.io/ast-for-javascript-developers-3e79aeb08343)。不多花時間特意翻譯一篇文章,咬文嚼字是件很累的事情,實在是這篇寫的太棒了,因此忍不住想和你們一塊兒分享。css
該譯文出自個人博客:https://github.com/CodeLittlePrince/blog/issues/19,個人博客會不定時更新各類類型文章,但願你們支持。java
OK,咱們直接進入正題。node
爲何要談AST(抽象語法樹)?react
若是你查看目前任何主流的項目中的 devDependencies,會發現前些年的不可勝數的插件誕生。咱們概括一下有:javascript轉譯、代碼壓縮、css預處理器、elint、pretiier,等。有不少js模塊咱們不會在生產環境用到,可是它們在咱們的開發過程當中充當着重要的角色。全部的上述工具,無論怎樣,都創建在了AST這個巨人的肩膀上。webpack
全部的上述工具,無論怎樣,都創建在了AST這個巨人的肩膀上。git
咱們定一個小目標,從解釋什麼是AST開始,而後到怎麼從通常代碼開始去構建它。咱們將簡單地接觸在AST處理基礎上,一些最流行的使用例子和工具。而且,我計劃談下個人js2flowchart項目,它是一個不錯的利用AST的demo。OK,讓咱們開始吧。es6
什麼是AST(抽象語法樹)?github
It is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.web
估計不少同窗會和圖中的喵同樣,看完這段官方的定義一臉懵逼。OK,咱們來看例子:
這很簡化。
實際上,正真AST每一個節點會有更多的信息。可是,這是大致思想。從純文純中,咱們將獲得樹形結構的數據。每一個條目和樹中的節點一一對應。
那怎麼從純文本中獲得AST呢?哇哦,咱們知道當下的編譯器都作了這件事前。那咱們就看看通常的編譯器怎麼作的就能夠了。
想作一款編譯器是個比較消耗髮量的事情,但幸運的是,咱們無需貫穿編譯器的全部知識點,最後將高級語言轉譯爲二進制代碼。咱們只須要關注詞法分析和預發分析。這兩步是從代碼中生成AST的關鍵所在。
第一步,詞法分析,也叫作掃描scanner。它讀取咱們的代碼,而後把它們按照預約的規則合併成一個個的標識tokens。同時,它會移除空白符,註釋,等。最後,整個代碼將被分割進一個tokens列表(或者說一維數組)。
當詞法分析源代碼的時候,它會一個一個字母地讀取代碼,因此很形象地稱之爲掃描-scans;當它遇到空格,操做符,或者特殊符號的時候,它會認爲一個話已經完成了。
第二步,語法分析,也解析器。它會將詞法分析出來的數組轉化成樹形的表達形式。同時,驗證語法,語法若是有錯的話,拋出語法錯誤。
當生成樹的時候,解析器會刪除一些不必的標識tokens(好比不完整的括號),所以AST不是100%與源碼匹配的,可是已經能讓咱們知道如何處理了。說個題外話,解析器100%覆蓋全部代碼結構生成樹叫作CST(具體語法樹)
咱們最終獲得的。
想要學習更多關於編譯器的知識?the-super-tiny-compiler,一個賊好的項目。大概200來行代碼,幾乎每行都有註釋。
想要本身建立門編程語言?LangSandbox,一個更好的項目。它演示瞭如何創造一門編程語言。固然,設計編程語言這樣的書市面上也一坨坨。因此,這項目更加深刻,與the-super-tiny-compiler的項目將Lisp轉爲C語言不一樣,這個項目你能夠寫一個你本身的語言,而且將它編譯成C語言或者機器語言,最後運行它。
我能直接用三方庫來生成AST嗎?
固然能夠!有一坨坨的三方庫能夠用。你能夠訪問astexplorer,而後挑你喜歡的庫。astexplorer是一個很棒的網站,你能夠在線玩轉AST,並且除了js,還有不少其它語言的AST庫。
我不得不強調一款我以爲很棒的三方庫,叫作babylon。
它被用在大名鼎鼎的babel中,也許這也是它之因此這麼火的緣由。由於有babel項目的支持,咱們能夠意料到它將與時俱進,一直支持最新的JS特性,咱們能夠放心大膽地用,不怕之後JS又出新版致使代碼的大規模重構。另外,它的API也很是的簡單,容易使用。
Ok,如今你知道怎麼將代碼生成AST了,讓咱們繼續,來看看現實中的用例。
第一個用例,我想談談代碼轉化,沒錯,就是那個貨,babel。
Babel is not a ‘tool for having ES6 support’. Well, it is, but it is far not only what it is about.
常常把beble和支持es6/7/8聯繫起來,實際上,這也是咱們常常用它的緣由。可是,它僅僅是一組插件中的一個。咱們也可使用它來壓縮代碼,react相關預發轉譯(如jsx),flow插件等。
babel是一個javascript編譯器。宏觀來講,它分3個階段運行代碼:解析(parsing),轉譯(transforming),生成(generation)。咱們能夠給babel 一些javascript代碼,它修改代碼而後生成新的代碼返回。那它是怎樣修改代碼的呢?沒錯!它建立了AST,遍歷樹,修改tokens,最後從AST中生成新的代碼。
咱們來從下面的demo中看下這個過程:
像我以前提到的,babel使用babylon,因此,首先,咱們解析代碼成AST,而後遍歷AST,再反轉全部的變量名,最後生成代碼。完成!正如咱們看到的,第一步(解析)和第三步(生成)看起來很是常規,咱們每次都會作這兩步。因此,babel接管處理了它倆。最後,咱們最爲關心的,那就是AST轉譯這一步了。
當咱們開發babel-plugin的時候,咱們只須要描述轉化你AST的節點「visitors」就能夠了。
將它加入你的babel插件列表中,設置你webpack的babel-loader配置或者 .babelrc中的plugins便可。
若是你想要學習怎麼建立你的第一個babel-plugin,能夠查看Babel-handbook
讓咱們繼續,下一個用例,我想提到的是自動代碼重構工具,以及神器JSCodeshift。
好比說你想要替換掉全部的老掉牙的匿名函數,把他們變成Lambda表達式(箭頭函數)。
你的代碼編輯器極可能無法這麼作,由於這並非簡單地查找替換操做。這時候jscodeshift就登場了。
若是你聽過 jscodeshift,你極可能也聽過 codemods,一開始挺這兩個詞可能很困惑,不過不要緊,接下來就解釋。jscodeshift是一個跑 codemods的工具。 codemod是一段描述AST要轉化成什麼樣的代碼,這思想和babel的插件一模一樣。
因此,若是你想建立自動把你的代碼從舊的框架遷移到新的框架,這就是一種很乃思的方式。舉個例子,react 16的prop-types重構。
有不少不一樣的 codemodes已經建立了,你能夠保存你須要的,以避免手動的修改一坨坨代碼,拿去揮霍吧:
https://github.com/facebook/jscodeshift
https://github.com/reactjs/react-codemod
最後一個用例,我想要提到Prettier,由於可能每一個碼農都在平常工做中用到它。
Prettier 格式化咱們的代碼。它調整長句,整理空格,括號等。因此它將代碼做爲輸入,修改後的代碼做爲輸出。聽起來很熟悉是嗎?固然!
思路仍是同樣。首先,將代碼生成AST。以後依然是處理AST,最後生成代碼。可是,中間過程其實並不像它看起來那麼簡單。
一樣,若是你想學習更多在美化打印背後理論,這裏有一本你能夠深刻的書 《A prettier printer》(http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)。
文章迎來尾聲,咱們繼續,今天最後一件事,我想說起的就是個人庫,叫作 js2flowchart(4.5 k stars 在 Github)。
顧名思義,它將js代碼轉化生成svg流程圖。
這是一個很好的例子,由於它向你展示了你,當你擁有AST時,能夠作任何你想要作的事。把AST轉回成字符串代碼並非必要的,你能夠經過它畫一個流程圖,或者其它你想要的東西。
js2flowchart使用場景是什麼呢?經過流程圖,你能夠解釋你的代碼,或者給你代碼寫文檔;經過可視化的解釋學習其餘人的代碼;經過簡單的js語法,爲每一個處理過程簡單的描述建立流程圖。
立刻用最簡單的方式嘗試一下吧,去線上編輯看看 js-code-to-svg-flowchart
你也能夠在代碼中使用它,或者經過CLI,你只須要指向你想生成SVG的文件就行。並且,還有VS Code插件(連接在項目readme中)
那麼,它還能作什麼呢?哇哦,我這裏就不廢話了,你們有興趣直接看這個項目的文檔吧。
OK,那它是如何工做的呢?
首先,解析代碼成AST,而後,咱們遍歷AST而且生成另外一顆樹,我稱之爲工做流樹。它刪除不少不重要的額tokens,可是將關鍵塊放在一塊兒,如函數、循環、條件等。再以後,咱們遍歷工做流樹而且建立形狀樹。每一個形狀樹的節點包含可視化類型、位置、在樹中的鏈接等信息。最後一步,咱們遍歷全部的形狀,生成對應的SVG,合併全部的SVG到一個文件中。
結尾
尋找和篩選資料着實辛苦,但願同窗們能夠多多支持!
英文:Bohdan Liashenko 譯文:歲月是把殺豬刀
segmentfault.com/a/1190000017152442