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 系列壽命還剩幾年?

TAG:编程 | JetBrains | 编程语言 |