【轉(zhuǎn)】 AST(抽象語法樹)

轉(zhuǎn)載:https://github.com/CodeLittlePrince/blog/issues/19

前言

首先空镜,先說明下該文章是譯文蔫慧,原文出自《AST for JavaScript developers》躏哩。很少花時間特地翻譯一篇文章,咬文嚼字是件很累的事情,實在是這篇寫的太棒了迎卤,所以忍不住想和大家一起分享贾虽。

OK定罢,我們直接進入正題。

為什么要談AST(抽象語法樹)?

如果你查看目前任何主流的項目中的devDependencies壳贪,會發(fā)現(xiàn)前些年的不計其數(shù)的插件誕生。我們歸納一下有:javascript轉(zhuǎn)譯寝杖、代碼壓縮违施、css預(yù)處理器、elint瑟幕、pretiier磕蒲,等。有很多js模塊我們不會在生產(chǎn)環(huán)境用到只盹,但是它們在我們的開發(fā)過程中充當(dāng)著重要的角色辣往。所有的上述工具,不管怎樣殖卑,都建立在了AST這個巨人的肩膀上站削。

所有的上述工具,不管怎樣孵稽,都建立在了AST這個巨人的肩膀上

我們定一個小目標(biāo)许起,從解釋什么是AST開始,然后到怎么從一般代碼開始去構(gòu)建它肛冶。我們將簡單地接觸在AST處理基礎(chǔ)上街氢,一些最流行的使用例子和工具。并且睦袖,我計劃談下我的js2flowchart項目珊肃,它是一個不錯的利用AST的demo。OK馅笙,讓我們開始吧伦乔。

什么是AST(抽象語法樹)?

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.

估計很多同學(xué)會和圖中的喵一樣董习,看完這段官方的定義一臉懵逼烈和。OK,我們來看例子:

這很簡化

實際上皿淋,正真AST每個節(jié)點會有更多的信息招刹。但是恬试,這是大體思想。從純文本中疯暑,我們將得到樹形結(jié)構(gòu)的數(shù)據(jù)训柴。每個條目和樹中的節(jié)點一一對應(yīng)。

那怎么從純文本中得到AST呢妇拯?哇哦幻馁,我們知道當(dāng)下的編譯器都做了這件事情。那我們就看看一般的編譯器怎么做的就可以了越锈。

想做一款編譯器是個比較消耗發(fā)量的事情仗嗦,但幸運的是,我們無需貫穿編譯器的所有知識點甘凭,最后將高級語言轉(zhuǎn)譯為二進制代碼稀拐。我們只需要關(guān)注詞法分析和語法分析。這兩步是從代碼中生成AST的關(guān)鍵所在对蒲。

第一步钩蚊,詞法分析,也叫做掃描scanner蹈矮。它讀取我們的代碼,然后把它們按照預(yù)定的規(guī)則合并成一個個的標(biāo)識tokens鸣驱。同時泛鸟,它會移除空白符,注釋踊东,等北滥。最后,整個代碼將被分割進一個tokens列表(或者說一維數(shù)組)闸翅。

當(dāng)詞法分析源代碼的時候再芋,它會一個一個字母地讀取代碼,所以很形象地稱之為掃描-scans坚冀;當(dāng)它遇到空格济赎,操作符,或者特殊符號的時候记某,它會認為一個話已經(jīng)完成了司训。

第二步,語法分析液南,也解析器壳猜。它會將詞法分析出來的數(shù)組轉(zhuǎn)化成樹形的表達形式。同時滑凉,驗證語法统扳,語法如果有錯的話喘帚,拋出語法錯誤。

當(dāng)生成樹的時候咒钟,解析器會刪除一些沒必要的標(biāo)識tokens(比如不完整的括號)啥辨,因此AST不是100%與源碼匹配的,但是已經(jīng)能讓我們知道如何處理了盯腌。說個題外話溉知,解析器100%覆蓋所有代碼結(jié)構(gòu)生成樹叫做CST(具體語法樹)

我們最終得到的

想要學(xué)習(xí)更多關(guān)于編譯器的知識?
the-super-tiny-compiler腕够,一個賊好的項目级乍。大概200來行代碼,幾乎每行都有注釋帚湘。

想要自己創(chuàng)建門編程語言玫荣?
LangSandbox,一個更好的項目大诸。它演示了如何創(chuàng)造一門編程語言捅厂。當(dāng)然,設(shè)計編程語言這樣的書市面上也一坨坨资柔。所以焙贷,這項目上一個相比更加深入,與the-super-tiny-compiler的項目將Lisp轉(zhuǎn)為C語言不同贿堰,這個項目你可以寫一個你自己的語言辙芍,并且將它編譯成C語言或者機器語言,最后運行它羹与。

我能直接用三方庫來生成AST嗎故硅?
當(dāng)然可以!有一坨坨的三方庫可以用纵搁。你可以訪問astexplorer吃衅,然后挑你喜歡的庫。astexplorer是一個很棒的網(wǎng)站腾誉,你可以在線玩轉(zhuǎn)AST徘层,而且除了js,還有很多其它語言的AST庫妄辩。

我不得不強調(diào)一款我覺得很棒的三方庫惑灵,叫做babylon。

它被用在大名鼎鼎的babel中眼耀,也許這也是它之所以這么火的原因英支。因為有babel項目的支持,我們可以意料到它將與時俱進哮伟,一直支持最新的JS特性干花,因此可以放心大膽地用妄帘,不怕以后JS又出新版導(dǎo)致代碼的大規(guī)模重構(gòu)。另外池凄,它的API也非常的簡單抡驼,容易使用。

Ok肿仑,現(xiàn)在你知道怎么將代碼生成AST了致盟,讓我們繼續(xù),來看看現(xiàn)實中的用例尤慰。

第一個用例馏锡,我想談?wù)劥a轉(zhuǎn)化,沒錯伟端,就是那個貨杯道,babel。

Babel is not a ‘tool for having ES6 support’. Well, it is, but it is far not only what it is about.

經(jīng)常把beble和支持es6/7/8聯(lián)系起來责蝠,實際上党巾,這也是我們經(jīng)常用它的原因。但是霜医,它僅僅是一組插件中的一個齿拂。我們也可以使用它來壓縮代碼,react相關(guān)語法轉(zhuǎn)譯(如jsx)支子,flow插件等创肥。

babel是一個javascript編譯器。宏觀來說值朋,它分3個階段運行代碼:解析(parsing),轉(zhuǎn)譯(transforming)巩搏,生成(generation)昨登。我們可以給babel 一些javascript代碼,它修改代碼然后生成新的代碼返回贯底。那它是怎樣修改代碼的呢丰辣?沒錯!它創(chuàng)建了AST禽捆,遍歷樹笙什,修改tokens,最后從AST中生成新的代碼胚想。

我們來從下面的demo中看下這個過程:

像我之前提到的琐凭,babel使用babylon,所以浊服,首先统屈,我們解析代碼成AST胚吁,然后遍歷AST,再反轉(zhuǎn)所有的變量名愁憔,最后生成代碼腕扶。完成!正如我們看到的吨掌,第一步(解析)和第三步(生成)看起來很常規(guī)半抱,我們每次都會做這兩步。所以膜宋,babel接管處理了它倆窿侈。最后,我們最為關(guān)心的激蹲,那就是AST轉(zhuǎn)譯這一步了棉磨。

當(dāng)我們開發(fā)babel-plugin的時候,我們只需要描述轉(zhuǎn)化你AST的節(jié)點“visitors”就可以了学辱。

將它加入你的babel插件列表中乘瓤,設(shè)置你webpack的babel-loader配置或者.babelrc中的plugins即可

You may check out Babel-handbook if you would like to learn more about how to build your first babel-plugin.
如果你想要學(xué)習(xí)怎么創(chuàng)建你的第一個babel-plugin,可以查看Babel-handbook

讓我們繼續(xù)策泣,下一個用例衙傀,我想提到的是自動代碼重構(gòu)工具,以及神器JSCodeshift萨咕。

比如說你想要替換掉所有的老掉牙的匿名函數(shù)统抬,把他們變成Lambda表達式(箭頭函數(shù))。

你的代碼編輯器很可能沒法這么做危队,因為這并不是簡單地查找替換操作聪建。這時候jscodeshift就登場了。

如果你聽過jscodeshift茫陆,你很可能也聽過codemods金麸,一開始挺這兩個詞可能很困惑,不過沒關(guān)系簿盅,接下來就解釋挥下。jscodeshift是一個跑codemods的工具。codemod是一段描述AST要轉(zhuǎn)化成什么樣的代碼桨醋,這思想和babel的插件如出一轍棚瘟。

所以,如果你想創(chuàng)建自動把你的代碼從舊的框架遷移到新的框架喜最,這就是一種很乃思的方式偎蘸。舉個例子,react 16的prop-types重構(gòu)。

有很多不同的codemodes已經(jīng)創(chuàng)建了禀苦,你可以保存你需要的蔓肯,以免手動的修改一坨坨代碼,拿去揮霍吧:
https://github.com/facebook/jscodeshift
https://github.com/reactjs/react-codemod

最后一個用例振乏,我想要提到Prettier蔗包,因為可能每個碼農(nóng)都在日常工作中用到它。

Prettier 格式化我們的代碼慧邮。它調(diào)整長句调限,整理空格,括號等误澳。所以它將代碼作為輸入耻矮,修改后的代碼作為輸出。聽起來很熟悉是嗎忆谓?當(dāng)然裆装!

思路還是一樣。首先倡缠,將代碼生成AST哨免。之后依然是處理AST,最后生成代碼昙沦。但是琢唾,中間過程其實并不像它看起來那么簡單。

同樣盾饮,如果你想學(xué)習(xí)更多在美化打印背后理論采桃,這里有一本你可以深入的書 《A prettier printer》

文章迎來尾聲丘损,我們繼續(xù)普办,今天最后一件事,我想提及的就是我的庫徘钥,叫做js2flowchart(4.5 k stars 在 Github)泌豆。

顧名思義,它將js代碼轉(zhuǎn)化生成svg流程圖

這是一個很好的例子吏饿,因為它向你展現(xiàn)了你,當(dāng)你擁有AST時蔬浙,可以做任何你想要做的事猪落。把AST轉(zhuǎn)回成字符串代碼并不是必要的,你可以通過它畫一個流程圖畴博,或者其它你想要的東西笨忌。

js2flowchart使用場景是什么呢?通過流程圖俱病,你可以解釋你的代碼官疲,或者給你代碼寫文檔袱结;通過可視化的解釋學(xué)習(xí)其他人的代碼;通過簡單的js語法途凫,為每個處理過程簡單的描述創(chuàng)建流程圖垢夹。

馬上用最簡單的方式嘗試一下吧,去線上編輯看看 js-code-to-svg-flowchart

你也可以在代碼中使用它维费,或者通過CLI果元,你只需要指向你想生成SVG的文件就行。而且犀盟,還有VS Code插件(鏈接在項目readme中)

那么而晒,它還能做什么呢?哇哦阅畴,我這里就不廢話了倡怎,大家有興趣直接看這個項目的文檔吧。

OK贱枣,那它是如何工作的呢监署?

首先,解析代碼成AST冯事,然后焦匈,我們遍歷AST并且生成另一顆樹,我稱之為工作流樹昵仅。它刪除很多不重要的額tokens缓熟,但是將關(guān)鍵塊放在一起,如函數(shù)摔笤、循環(huán)够滑、條件等。再之后吕世,我們遍歷工作流樹并且創(chuàng)建形狀樹彰触。每個形狀樹的節(jié)點包含可視化類型、位置命辖、在樹中的連接等信息况毅。最后一步,我們遍歷所有的形狀尔艇,生成對應(yīng)的SVG尔许,合并所有的SVG到一個文件中。

結(jié)尾

尋找和篩選資料著實辛苦终娃,希望同學(xué)們可以多多支持味廊!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子余佛,更是在濱河造成了極大的恐慌柠新,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辉巡,死亡現(xiàn)場離奇詭異恨憎,居然都是意外死亡,警方通過查閱死者的電腦和手機红氯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門框咙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人痢甘,你說我怎么就攤上這事喇嘱。” “怎么了塞栅?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵者铜,是天一觀的道長。 經(jīng)常有香客問我放椰,道長作烟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任砾医,我火速辦了婚禮拿撩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘如蚜。我一直安慰自己压恒,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布错邦。 她就那樣靜靜地躺著探赫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撬呢。 梳的紋絲不亂的頭發(fā)上伦吠,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機與錄音魂拦,去河邊找鬼毛仪。 笑死,一個胖子當(dāng)著我的面吹牛芯勘,可吹牛的內(nèi)容都是我干的潭千。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼借尿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起路翻,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤狈癞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后茂契,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝶桶,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年掉冶,在試婚紗的時候發(fā)現(xiàn)自己被綠了真竖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡厌小,死狀恐怖恢共,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情璧亚,我是刑警寧澤讨韭,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站癣蟋,受9級特大地震影響透硝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜疯搅,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一濒生、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幔欧,春花似錦罪治、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瘦麸,卻和暖如春谁撼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滋饲。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工厉碟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屠缭。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓箍鼓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親呵曹。 傳聞我的和親對象是個殘疾皇子款咖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容

  • babel官網(wǎng) babel 介紹 Babel 是一個通用的多用途 JavaScript 編譯器何暮。通過 Babel ...
    鋒享前端閱讀 1,826評論 0 10
  • AST簡介 AST(Abstract syntax tree)即為“抽象語法樹”,是編輯器對代碼的第一步加工之后的...
    North_2016閱讀 11,305評論 3 35
  • Babel是前端很常用的轉(zhuǎn)碼器铐殃,更準(zhǔn)確地說是轉(zhuǎn)譯器海洼,是從源碼到源碼的轉(zhuǎn)換編譯器,例如可以將我們按照ES6標(biāo)準(zhǔn)寫的代...
    拉面頭_7c92閱讀 1,204評論 4 2
  • 為什么要用babel轉(zhuǎn)換代碼 我們之前做一些兼容都會都會接觸一些 Polyfill 的概念富腊,比如如果某個版本的瀏覽...
    漂_34c2閱讀 1,416評論 0 2
  • What is AST 什么是AST?AST是Abstract Syntax Tree(抽象語法樹)的縮寫坏逢。傳說中...
    ronniegong閱讀 3,745評論 0 11