日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Sweet.js 用 Readtables 编译 JSX

發(fā)布時(shí)間:2025/3/17 javascript 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Sweet.js 用 Readtables 编译 JSX 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

from http://jlongster.com/Compiling-JSX-with-Sweet.js-using-Readtables

JSX http://facebook.github.io/react/docs/jsx-in-depth.html 是一個(gè) Facebook 項(xiàng)目,給 js 嵌入了 xml-like 語言,它是 React http://facebook.github.io/react/ 中標(biāo)志性的使用特色。很多人喜歡它并且發(fā)現(xiàn)它非常好用。不幸的是他需要獨(dú)立的編譯器,并且不能和其他語言混合或者擴(kuò)展。我用 sweet.js 宏 http://sweetjs.org/ 實(shí)現(xiàn)了一個(gè) JSX “編譯器” https://github.com/jlongster/jsx-reader,因而你可以把 jsx 和其他任何宏語言擴(kuò)展一起放手邊隨時(shí)供使用。

我預(yù)見,預(yù)見有人能給 js 語言添加一些難懂的特性,比如模式匹配 https://github.com/natefaubion/sparkler ,然后我只需要安裝模塊然后使用它。在 js 影響深遠(yuǎn)的今天,我認(rèn)為這種語言擴(kuò)展能力是非常重要滴。

這很重要,不僅僅是因?yàn)槲一蛘吣隳軗碛性?goroutines https://gobyexample.com/goroutines 或者 原生持久數(shù)據(jù)解構(gòu) http://swannodette.github.io/mori/ 語法 。它更令人難以置信的是,我們用的野生的特性,可能成為 ES 標(biāo)準(zhǔn)的一部分,甚至成為標(biāo)準(zhǔn)的 JS 的一部分。未來的 js 會(huì)由于我們的反饋?zhàn)兊酶谩N覀冃枰K化的方式來擴(kuò)展 js,一但這個(gè)可行,會(huì)無縫的與無數(shù)的擴(kuò)展共享成果。

我不準(zhǔn)備解釋為啥 sweet.js 宏是這個(gè)目標(biāo)的答案。如果你想聽更多,看我的 JSConf 2014 演講 https://www.youtube.com/watch?v=wTkcGprt5rU 。如果你準(zhǔn)備寫負(fù)面的評(píng)論,請(qǐng)先閱讀這篇 http://jlongster.com/Compiling-JSX-with-Sweet.js-using-Readtables#concerned

我為啥在 sweet.js 中實(shí)現(xiàn)一個(gè) JSX 呢?如果你使用 JSX,現(xiàn)在你可以在任何其他的可用宏 https://www.npmjs.org/search?q=sweet-macros 邊上使用 jsx-reader https://github.com/jlongster/jsx-reader。想要原生語法給持久化數(shù)據(jù)結(jié)構(gòu)?繼續(xù)讀下去。。。

遇到的問題

jsx 這么工作:xml 元素轉(zhuǎn)換為簡(jiǎn)單的 js 對(duì)象。

var div = <div><h1>{ header }</h1> </div>;

這轉(zhuǎn)化為:

var div = React.Dom.div(null, React.DOM.h1(null, header));

http://sweetjs.org/ 直到這周 jsx 都無法把它實(shí)現(xiàn),是什么原因讓這做到了? 可讀表 Readtables http://sweetjs.org/doc/main/sweet.html#reader-extensions

我簡(jiǎn)單解釋下 sweet.js 的一些技術(shù)環(huán)境。Sweet.js 主要工作在口令層面,不是 AST,AST是唯一能擴(kuò)展語言的組成部分的方式 見 *。其中的算法大量的來自 10 多年的 Lisp Scheme 社區(qū)尤其是 Racket http://docs.racket-lang.org/ 的工作基礎(chǔ)之上

我們的想法是,你在一個(gè)輕量的口令樹之上提供語言的宏定義,然后展開這些口令。定制的模式匹配 http://sweetjs.org/doc/main/sweet.html#rule-macros 和 全自動(dòng)衛(wèi)生 http://sweetjs.org/doc/main/sweet.html#hygiene 讓它可以做非常復(fù)雜的擴(kuò)展,并且你可以從 sourcemaps 上獲得更多。

生成的管道看起來像這樣:

  • read 讀一段字符串代碼,產(chǎn)生一個(gè)口令樹。它會(huì)消除歧義的正則表達(dá)式語法,分類,以及其他的有趣的東西。你會(huì)得到一個(gè)很漂亮的口令樹,上面是原子的語法片段。它是一顆用 {}()[] 作為葉子來作為分隔符口令的樹。
  • expand 遍歷口令樹,展開任何找到的宏。它用名字查找宏,用剩下的語法來調(diào)用宏。這個(gè)階段還會(huì)做一個(gè)輕快的解析,比如指出一些不合法的表達(dá)式
  • parse 處理展開后的樹,生成一些真正的 js AST。目前 sweet.js 使用了一個(gè)打了一些補(bǔ)丁的 esparima 版本來做這個(gè)工作
  • generate 從 AST 生成最后的 js 代碼。 sweet.js 使用 escodegen 來做這個(gè)工作

expand 階段本質(zhì)上是給語言解析器添加了展開性。這對(duì)很多特性都很好。比如 類型 模塊 等那些需要整個(gè)程序的信息,還有那些在解析 parse 階段或者生成階段最好有 AST 支持的特性。現(xiàn)在我們還沒有能展開到那個(gè)階段。

很少有特性需要擴(kuò)展到 read 階段, JSX 就是其中之一。 Lisp 早就有一個(gè)叫做 readtables 的東西了,我最近意識(shí)到我們的 js 需要類似的東西,以支持 JSX 的實(shí)現(xiàn)。因此我實(shí)現(xiàn)了它 https://github.com/mozilla/sweet.js/pull/340 !

有幾個(gè)原因 jsx 需要作為一個(gè) reader 生效而不是作為宏:

  • 最重要的,閉合標(biāo)簽 是完全無效的 js 語法。默認(rèn)的 reader 認(rèn)為這開始了一個(gè)正則表達(dá)式,但是他不能找到它的結(jié)束符號(hào)。因此他直接在 read 階段拋出錯(cuò)誤
  • jsx 有非常特殊的關(guān)于空白的處理規(guī)則。空白元素需要被保存,例如 兄弟表達(dá)式之間插入了一個(gè)空格。宏不知道任何空白的故事。
  • Reader 擴(kuò)展允許你安裝一個(gè)自定義的 reader 當(dāng)在源碼中遇到一個(gè)特殊的單詞的時(shí)候調(diào)用。你能讀取你需要的足夠多的源碼,然后返回一個(gè)口令組。reader 擴(kuò)展只能被用標(biāo)點(diǎn)的 punctuators 觸發(fā)。(比如 < % 等符號(hào)),因此你不能做比如改變引號(hào)起作用的方式這類可怕的事情。

    一個(gè)可讀表是一個(gè)給 reader 擴(kuò)展用的字符表。在這里 http://sweetjs.org/doc/main/sweet.html#reader-extensions 的這個(gè)文檔中來深入了解。

    • 關(guān)于這點(diǎn)我有許多思考和論點(diǎn),我會(huì)在未來的文章中展開來說。在 AST 層面工作是一個(gè)很棒的特性,這需要整個(gè)程序,比如類型和代碼分析的優(yōu)化。看這篇 http://blog.fogus.me/2012/04/25/the-clojurescript-compilation-pipeline/ 來了解關(guān)于這個(gè)想法的一個(gè)非常好的解釋。

    npm 上可用的: jsx-reader

    現(xiàn)在我們有了可讀表,我們可以實(shí)現(xiàn) jsx 啦!我已經(jīng)用 jsx-reader https://github.com/jlongster/jsx-reader 做到了。他是一個(gè)文字的 jsx 編譯器端口,機(jī)智的做到了所有的空白規(guī)則以及其他的邊緣情況。(希望如此)

    加載一個(gè) reader 擴(kuò)展,傳入模塊名給 sweet.js 用 -l 編譯 sjs 。這是所有的步驟:

    $ npm install sweet.js $ npm install jsx-reader $ sjs -l jsx-reader file.js

    當(dāng)然你也可以加載任何其他的宏,一起作用在你的文件上。這是一個(gè)組合的很漂亮的語言擴(kuò)展(試試 es6-macroshttps://github.com/jlongster/es6-macros)

    我已經(jīng)創(chuàng)建了一個(gè) webpack loader https://github.com/jlongster/sweetjs-loader 和一個(gè) gulp loader https://github.com/jlongster/gulp-sweetjs 而且是最新的支持 readtable 加載的。

    這是測(cè)試版的軟件我在一些小的原生的 jsx 編譯器的測(cè)試用例上測(cè)試通過了,此外它也在一些大文件上工作良好。However,依然有一些小 bugs 和邊緣情況需要被人們發(fā)現(xiàn)他之后修補(bǔ)。

    你不僅僅能獲得可靠的 sourcemaps (傳入 -c 給 sjs),而且你也會(huì)比原生 jsx complier 有更好的錯(cuò)誤提示。例如:如果你忘了關(guān)閉一個(gè)標(biāo)簽:

    var div = <div><h1>Title</h1><p> </div>

    你會(huì)得到一個(gè)漂亮的,感覺的錯(cuò)誤信息,明確的指出你的代碼中的錯(cuò)誤

    SyntaxError: [JSX] Expected corresponding closing tag for p 4: </div>^at Object.readtables.readerAPI.throwSyntaxError (/Users/james/tmp/poop/node_modules/sweet.js/lib/parser.js:5075:23)

    有一個(gè)退步就是,這比原生的 jsx compiler 慢。一個(gè) 2000 行代碼的大文件會(huì)花費(fèi) ~.7s 來編譯(排序了預(yù)熱時(shí)間,因?yàn)槟銜?huì)在大多數(shù)項(xiàng)目中使用監(jiān)聽器 watcher),原生的只要 ~0.4s。實(shí)際上,這不是特別明顯,因?yàn)榇蠖鄶?shù)文件是很多的更小的東西,大部分在幾百毫秒就編譯完了。當(dāng)然,sweet.js 會(huì)更努力滴優(yōu)化這個(gè)時(shí)間滴。

    例子: 持久化數(shù)據(jù)結(jié)構(gòu)

    React 和持久化數(shù)據(jù)結(jié)構(gòu)一起工作會(huì)表現(xiàn)得更好。問題是 js 原生沒有它們,但是幸運(yùn)的是有 mori http://swannodette.github.io/mori/ 這些庫可用。只是你再也不能用對(duì)象字面值了;你不得不 mori.vector(1,2,3) 來代替 [1,2,3];

    如果我們給 mori 實(shí)現(xiàn)一個(gè)字面的語法會(huì)怎樣?可能你就用 #[1,2,3] 來創(chuàng)建持久的矢量,而 #{x: 1, y: 2} 來創(chuàng)建持久的表 map 。那也太棒了吧!(不幸的是我現(xiàn)在還沒做到,但我非常渴望做到這點(diǎn)。。。)

    現(xiàn)在每個(gè)用 jsx 的人都可以在 React 中用我的字面語法來持久化數(shù)據(jù)結(jié)構(gòu)。這真的是一個(gè)給力的工具包。

    jsx 的 read 算法

    給 js 添加新的語法,特別擁有大塊展示區(qū)域的比如 jsx。必須仔細(xì)的實(shí)現(xiàn)。它必須 100% 向后兼容,并且和 ES.next 特性一樣在腦中完成。

    jsx-reader 和 原生 jsx 編譯器都查找 < 口令并觸發(fā)一個(gè) jsx 表達(dá)式的解析。雖然這個(gè)有一個(gè)關(guān)鍵的不同。你可能注意到 jsx-reader 作為一個(gè) reader,被源代碼調(diào)用的時(shí)候是沒有環(huán)境的。原生的 jsx 編譯器用被猴子們打了補(bǔ)丁 monkeypatches 的 esprima 來調(diào)用 < ,當(dāng)解析一個(gè)表達(dá)式的時(shí)候,因此它更容易保證正確的解析。 < 在 js 表達(dá)式中從不會(huì)有效,因此它能這么用它。

    jsx-reader 也是用 < 調(diào)用,當(dāng)它在源碼的任何地方出現(xiàn)的時(shí)候,甚至是比較大小的操作符的時(shí)候。這讓人提心吊膽的;我們需要更仔細(xì)。但是我想到了一個(gè)可行的算法。你不會(huì)一直需要一個(gè) ast。

    jsx-reader 開始解析 < 然后任何它后面的東西作為 jsx 表達(dá)式,如果它遇到一些確定不期望的點(diǎn),它停下來。大多數(shù)時(shí)候,它能很早的發(fā)覺 < 之后是或者不是 jsx 表達(dá)式。算法來了:

  • Read <
  • Read 一個(gè)標(biāo)識(shí)符 失敗了 bail
  • If 接下來的不是 > :

  • 跳過空白
  • 讀取一個(gè)標(biāo)識(shí)符。 失敗了 bail
  • if 下一個(gè)是 > 跳轉(zhuǎn)到 4.1
  • 讀取 =
  • if 下一個(gè)是 { 讀取 js 表達(dá)式 類似 4.3.1
  • if 下一個(gè)是 < 跳轉(zhuǎn)到 1
  • 否則,讀取一個(gè)字符串
  • if 下一個(gè)是 > 跳轉(zhuǎn)到 4.1
  • 否則 跳轉(zhuǎn)到 3.1
  • 否則:

  • 讀取 >
  • 讀取任何原始文本直到 { 或者 <
  • if 下一個(gè)是 {
  • 讀取 {
  • 讀取所有的 js 口令直到 }
  • 讀取 }
  • 跳轉(zhuǎn)到 4.2
  • if 下一個(gè)是 <:
  • if 下一個(gè)是 < 和 / 跳轉(zhuǎn)到 5
  • 否則 跳轉(zhuǎn)到 1
  • if 是文件的底部, bail
  • 讀取 <
  • 企圖讀取一個(gè)正則。如果成功了 bail
  • 讀取 /
  • 讀取一個(gè)標(biāo)識(shí)符 確保他匹配了打開的標(biāo)識(shí)符
  • 讀取>
  • 我很快的打出了這些,可能有更好的方式啦,但是你應(yīng)該了解了這思路。主要是一般情況下很容易消除歧義,但是所有的邊緣情況都需要生效。邊緣情況不是特別高性能,因?yàn)槲覀兊?reader 可能要做很多工作然后丟棄它,但是 99.9% 的情況下不會(huì)發(fā)生這種事情。

    在我們的算法中,如果我們說 read 而不說相應(yīng)的的 “如果失敗了 bail”,它會(huì)拋出一個(gè)錯(cuò)誤。我們依然可以給出好的錯(cuò)誤提示,當(dāng)遇到二義性的 js 邊緣情況的時(shí)候。

    這兒有幾個(gè)我們的 reader bails的地兒:

    • if (x < y) {} 它 bails 因?yàn)樗檎乙粋€(gè) y 后面的屬性 以及 ) 不是一個(gè)合法的標(biāo)識(shí)符字符
    • if (x < y > z) {} 它 bails 是因?yàn)樗?reads 所有的到文件末尾的方式但沒有找到一個(gè)閉合的標(biāo)簽。這只發(fā)生在頂級(jí)的元素上,而且是個(gè)性能的最差的情況。但是 x < y > z 不會(huì)做你想要的,而且沒人會(huì)這么用
    • if (x < div > y < /foo> /) {} 這是最難懂的情況,他是完全有效的 js 。它 bails 因?yàn)樗詈笞x取了一個(gè)正則

    我們獲取這個(gè)實(shí)際的優(yōu)點(diǎn):表達(dá)式像 x < y foo 在 js 中不會(huì)存在。這兒,它既不找 = 來解析屬性,也不找 > 來關(guān)閉元素,而是直接報(bào)錯(cuò),如果它沒找到的話。

    你擔(dān)心嗎?

    宏調(diào)用,弄混了一些人,還有很多遲疑的認(rèn)為它們是好用的東西。你可能也這么想,而且爭(zhēng)論一件事情:像 readtables 這樣的東西標(biāo)志著我們進(jìn)入了很深的地方。

    我請(qǐng)你們仔細(xì)想想 sweet.js。給它 5 分鐘。也許幾個(gè)小時(shí)。玩玩它:設(shè)置一個(gè) gulp 監(jiān)控 wathcer,從 npm 安裝一些宏并且用它。別直接反對(duì)它你實(shí)際上除非你理解了我們嘗試用它來解決的一些問題。許多人們給出的爭(zhēng)論都沒有很容易體會(huì)到那種場(chǎng)景(但有一些有!)

    不管那些,甚至你認(rèn)為這是錯(cuò)誤的方法。他當(dāng)然是有效的。軟件工業(yè)對(duì)我老說最困惑的一件事情就是:我們對(duì)其他人到底能有多惡毒,所以請(qǐng)做些建設(shè)性的事情。

    今天就試試 jsx-reader https://github.com/jlongster/jsx-reader 如果發(fā)現(xiàn) bugs https://github.com/jlongster/jsx-reader/issues

    以上;

    總結(jié)

    以上是生活随笔為你收集整理的Sweet.js 用 Readtables 编译 JSX的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。