MPS教程四:製作一個簡易語言(下)
在上一篇教程中,我們已經體驗了MPS創建語言的所必須擁有的完整流程。 我們也注意到了生成的語言語法很醜。
接下來,我們將編寫自己的Editor,把語法變得好看起來。
這篇文章結束之後,我這個MPS系列就暫時告一段落了,下一波教程可能很快出來,也有可能要過很久。不過我是不會跳票的!
最早我在寫這個系列的時候,就是抱著「反正肯定沒人看,不過我就是要寫」的任性態度寫的,結果萬萬沒想到,有好幾個人都過來跟我說「在讀你的MPS系列」,讓我內心小鹿亂撞,真是太驚喜了!所以說我按理說還是會堅持下去的!
為了獲得更好的觀看體驗以及去除馬賽克,可以轉移到我的博客MPS教程四:製作一個簡易語言(下)去看。
準備工作
- 閱讀上上篇博客並完成對應的工作
- 閱讀上一篇博客並完成對應的任務
- 英文輸入法(就是Ctrl+Space沒有被佔用的輸入法)
本文主要內容
- MPS的Editor
- 加深對MPS原理的理解
有哪些新概念
- 沒有新引入的概念
回顧一些概念
MPS
MPS是一個DSL開發環境,採用LOP的理念,將模塊化的組件作為一個個的DSL。MPS的「語言」對應OOP的「對象」、PP的「過程」。
MPS將程序保存為AST,在文件系統中序列化為xml保存,在編輯器中渲染為「代碼」。
你看到的,是MPS根據AST渲染出來的類似代碼的東西,但是它實際上不是代碼。
Concept
Concept可以當成是Java裡面的class,它可以有interface concept(對應Java的interface), 可以有abstract concept(對應Java的abstract class)。
Concept的實例就是AST的節點,對應class的實例是對象。讀者可以把AST節點理解為「Concept這種類似class的東西的實例」。
Generator
Generator遍歷AST節點,導出另一份代碼。
我們寫過的東西
Concept:
- Println,有一個叫content的property,類型是string
- PrintlnSet,有很多Println的children
Generator:
- 給PrintlnSet提供的簡單Generator
開始吧
打開你的MPS,進入上次創建的工程。
關於Editor
Editor是你的DSL的語法。
一般情況下,每個Concept都有一個對應的Editor。每個Editor有它適用的Concept。
Editor有兩種創建方式,一種是直接創建,一種是針對一個Concept創建。
在你的DSL中,每個AST節點(每個Println是一個AST節點,每個PrintlnSet也是AST節點)都需要一個對應的Editor, 然後MPS根據這個Editor來渲染出你看到的「代碼」。
Concept有介面Concept來實現組件化,所以Editor也可以組件化——你可以做一個子Editor,讓它「可被應用於」一個Abstract/Interface Concept,
然後你就可以在這個Concept的實現中「引用」這個Editor。
默認Editor
如果不為一個Concept指定Editor,那麼MPS會創建一個默認的Editor,這個Editor長啥樣讀者在上期已經看到了。
我們在本期會為我們的Concept定製Editor。
創建Editor
我們就直接針對Concept創建了。
然後進入我們在第一篇教程中創建的「Concept」,選擇Println這個Concept,打開並創建它對應的Editor。
(注意,在空白處右鍵就可以看到截圖裡的pop-up了)
然後你就可以看到你的第一個Editor啦!
最簡Editor
我們可以直接創建一個最最簡單的Editor,先在那一片姨媽紅中Ctrl+Space,選擇
{content}n
那個:
這表示,在這個位置渲染Println這個Concept的content屬性。還記得嗎?Println這個Concept的定義。
然後我們編譯一下,回到我們之前在Sandbox裡面寫的PrintlnSet:
看到了木有!我們自己定義的Editor替換掉了原來的默認Editor。
我們沒有動自己的代碼,而是改變了Editor,然後代碼的樣子就改變了。
也就是說,代碼寫成啥樣不重要,只要邏輯(AST)不變,你可以隨便改變語法,顯示出來的代碼就會隨之改變。
注意體會一下我之前很早就說過的「MPS顯示的其實是渲染的AST」。再次提醒自己,你在屏幕上看到的不是文本。
是不是有點懵
沒錯我似乎沒怎麼解釋Editor工作的機制。
MPS會遍歷這個AST的每個節點,針對每個節點,尋找它的Editor,然後根據Editor繪製這個節點。
在這個例子中,我們的Editor就只有一個 {content} 元素,因此MPS也就直接把這些節點的content屬性渲染出來了。
我們需要一個更加人性化的Editor,起碼得有個提示語吧?比如:
text: {content}n
類似這樣的如何?
MPS的Editor
MPS的Editor默認只有一個用來放元素的位置,叫做Editor的根元素。一般情況下,這個根元素會放一個集合, 在集合裡面再挨個添加我們需要的元素,而不是像我們剛才那樣直接把一個 {content} 放進去。
稍微好看點的Editor
我們需要一個Editor元素的集合。
於是我們在那一片姨媽紅中Ctrl+Space,然後輸入一個減號,選擇collection (indent):
然後我們就有了一個用來放元素的集合了。
我們可以在集合中間輸入一個提示語。
輸入完然後回車,這樣MPS會創建一個新元素,並進入之:
然後我們在這個元素中再放入 {content} (注意不能直接輸入 {content} ,必須使用Ctrl+Space後代碼補全裡面給出的 {content})。 最終的樣子應該是這樣:
然後編譯一下,回去看我們的Sandbox里的PrintlnSet。
可以看到,效果已經出來了:
稍微複雜點的Editor
我們還需要為PrintlnSet創建一個Editor。考慮如下因素:
- PrintlnSet有很多個Println,這個數目是不定的
- 我們需要一個提示語
- 元素豎著排列
(想了半天發現好像就這幾個因素)
然後我們可以考慮直接使用MPS提供的默認方法來創建這個Editor,就是這個:
然後它會自己出來一堆東西,我們不管它,先編譯看效果:
它直接把所有的Println全部塞一行了!這樣是不行的!我們需要一個豎著的!
關於 ChildNodeList
child node list是MPS提供來放置「數量不定的AST子節點」的Editor元素,它有橫著的,也有豎著的。
所以我們來,先弄個提示語,然後選擇child node list(vertical),這個就是豎著的ChildNodeList。
然後我們看到它出現了一堆元素。不要慌,我們挨個填。
首先,在 <no link> 這個地方填入你要link到的AST子節點——在這裡,就是我們的 clauses 。
然後編譯一下。
最後的樣子
回到Sandbox裡面,我們可以看到這個東西的語法又變了,變成了我們想要的樣子:
set: text: ***馬賽克***馬賽克***馬賽克***!!!!n text: My name is Van, Im an artist.n text: Im a performance artist.n
其他Editor功能
比如我們想把那些text換到下一行去,可以在 ( 上使用Alt+Enter,然後選擇「Add On New Line」:
貼圖真的很累,我就不截操作之後的效果圖了,反正這樣處理之後代碼是這樣的:
set: ntext: ***馬賽克***馬賽克***馬賽克***!!!!ntext: My name is Van, Im an artist.ntext: Im a performance artist.n
其實Editor還有更多功能,遠不止這點。
本文沒有講到的點
真的是篇幅所限!我其實很想把這些都說完的。
- 類似括弧配對的效果,可以對你指定的兩種(任意!不一定是括弧!可以是begin end這種)Editor元素進行高亮配對
- 改變前景/背景顏色(可以參考我之前的那個ShapeLang裡面的效果)
- 根據AST節點屬性的情況,按需顯示Editor元素
- 顯示錶格
- 奇怪的對齊方式
- 顯示圖片
- 顯示Swing控制項
還有很多別的,我現在腦子有點亂,就不一一列舉了。
還能這麼玩
我稍微改了下這個Editor的樣子,最終渲染出來的代碼是這樣的,可以作為作業,讀者可以試著自己弄一個出來:
看到了嗎?模仿Java。我們還可以再改改:
看到了嗎?模仿C#。
而進行這樣的語法改動,只需要動Editor,完全不需要動代碼。
結束
那麼MPS的最最簡單的入門教程就這麼結束啦~
我也得睡覺了,休息可是很重要的呢DAZE~
推薦閱讀:
※【認真臉】TypeScript 不是強類型!只是靜態類型!
※第3篇:「來啊,造作吧,反正有大把的內存」
※宏名不能隨便起
※增長黑客們都在使用什麼編程語言?
※Python 2 系列壽命還剩幾年?