踏入新的程式語言的世界  —  Goby ❤

Goby  — New Programming Language Created by Stan Lo for Micro-services

Goby 介紹

Goby  —  一個嶄新的程式語言,其創造者  —  Stan Lo 使用了 Go 語言實作並啟發自 Ruby 漂亮的語法。 Goby 擁有許多的特色,包含內建的 HTTP Server, 使用 Go 語言的特性處理 Thread 以及 Channel 以及它的 Plugin System 可以讓你藉由 Goby 輕鬆地操控 Go 裡面的資料。也就是說,你可以在你的 Goby 專案裡面同時使用 Go 的模組!

此外,由於 Goby 是參考自 Ruby 的語法(儘管 Goby 不完全百分之百相容於 Ruby),在大部分的狀況下,你可以直接把 Ruby 程式碼運行於 Goby 的專案!我是 Goby 專案的貢獻者之一,你可以點我進到一個簡單的網站,其後端完全是由 Goby 語言打造出來的,其程式碼長得幾乎跟 Ruby 的 Sinatra 專案一模一樣!

從一個前端來的開發者,但是…

我是如何去貢獻這個創建出新的程式語言的專案?

打從一開始,我覺得這事情就非常的瘋狂,因為在我加入這個專案的同時,我對於程式語言的架構完全是毫無頭緒的,畢竟自己是從前端的世界來的。然而,在那段期間,我不知怎地覺得要往一個新的方向鑽研資訊領域的世界。我認為那些可以架構出一個新的程式語言並在這個軟體世界建立新的規則的人就像是一個可以自由變出花樣的強大的魔術師。

有一天,當 Goby 的創作者在介紹他的專案時,我有一股強大的念頭想要成為這個專案的貢獻者。然而,在此同時,我也是非常緊張的,因為我是在我對這個創作出程式語言的領域無知的狀況下加入這個專案的。不過這也讓我在這趟貢獻的旅程裡加入更多刺激的元素。實際上,學習最基本的程式架構是簡單的而且很容易理解其原理。(當然,要實做出來是另一個故事~)

程式語言的架構

從你已經了解的觀念開始

我們從一個最簡單的例子來想,就算這不是程式語言而是人類熟知的語言。那們我們可以問一個很簡單的問題:要如何知道一個句子裡的英文語法結構?

Sentence: "Alexius dreams to become a DJ."

憑著你的直覺,你第一件想到的事情就是拆掉這個句子裡所有的字!

Words: ["Alexius", "dreams", "to", "become", "a", "DJ", "."]

語法解析

現在,想像如果我們要理解這個句子的英文語法結構,你必須知道該字的意思以及它的詞性。舉一個簡單的例子,『蘋果』這個詞是一個名詞(Noun) —  它是一種屬於薔薇科,外型圓潤的水果,通常會有薄薄的一層青綠色或者鮮紅色的表皮包覆在他脆脆的果肉上。(從來沒有想過一個名詞的解釋可以那麼地精確細緻)

總而言之,這個找出其詞性(或特性)並將它標示起來的過程稱之為符碼化(Tokenization,暫且這樣翻譯)此外,在英文裡面也有另一個詞叫 Lexer (詞法分析器),幾乎跟 Tokenization 具同等意思,所以你可以視它們為同義詞。我們將會開始把句子裡每一個字一個一個經過符碼化的處理。

第一個字 "Alexius" 很明顯地不是什麼動詞 (Verb)、形容詞 (Adjective)或者是介詞 (Preposition)等,直覺給你的解答是名詞 (Noun),於是 這個字經過符碼化的結果如下。

{ value: "Alexius", type: "noun" }

第二個字 "dreams" 是一種動詞,所以我們可以表示為以下的符碼。

{ value: "dreams", type: "verb" }

但實際上, “dreams” 這個字也可以是個名詞,不過由於要簡化到一個易懂的範例讓你可以有一個概念知道程式語言是如何被處理的,所以我們暫且將這個符碼視為動詞。

第三個字  —  to 開始變有趣了,由於在英文文法裡,它可以是不定詞 (infinitive,就是 to + 原形動詞)又或者是介詞 ,那我們暫且先給他一個名為 identifier 的型別,這就在程式語言裡很像關鍵字詞的角色。

{ value: "to", type: "identifier" }

然後我們迭代剩餘的字詞,我假定得到的結果如下。

Tokenized Result:
[
  { value: "Alexius", type: "noun"       },
  { value: "dreams",  type: "verb"       },
  { value: "to",      type: "identifier" },
  { value: "become",  type: "verb"       },
  { value: "a",       type: "article"    },
  { value: "DJ",      type: "noun"       },
  { value: ".",       type: "period"     }
]

使用 LALR 演算法解析語法

下一步就是要去分析並處理這些符碼化物件了,這一步在資訊領域稱之為 parsing (解析)。我們要使用一個名為 LALR 語法分析演算法由左而右地開始處理一個一個的符碼物件,在此同時,有必要的話我們還會“往回看”處理過後的符碼組合有沒有符合到一些基本的英文語法結構

為了要簡單地呈現出這個概念,我們從一第一個符碼物件開始移轉(Shift)至“英文語法堆疊”裡。

[ "Alexius" ] "dreams" "to" "become" "a" "DJ" "."
[  (noun)   ] ACTION: SHIFT

恩…看來第一個字我們都知道 “Alexius” 可能是英語語法結構裡整個句子的主詞,但我們還不太清楚,所以我們繼續移轉下一個符碼物件吧!

[ "Alexius" "dreams" ] "to" "become" "a" "DJ" "."
[  (noun)    (verb)  ] ACTION: SHIFT

在這一個階段,現在的符碼物件為 “dreams”,我們可以 “往前看” 並且似乎可以開始做英文句法比對了。這個結構是為最簡單的 主詞(一個名詞) + 動詞 的組合,因此我們可以進行簡化(Reduce)的動作。

[ "Alexius dreams" ] "to" "become" "a" "DJ" "."
[  (Sub. + Verb)   ] ACTION: REDUCE

那我們繼續移轉下一個符碼物件,於是得到以下的結果。

[ "Alexius dreams"     "to"     ] "become" "a" "DJ" "."
[  (Sub. + Verb)   (identifier) ] ACTION: SHIFT

由於這一次我們往前看還是不太知道 “to” 到底這時候是什麼樣的語法結構,它還是有可能為不定詞或者是介詞。因此,這個英文語法堆疊維持一個穩定的狀態,所以我們還是移轉下一個符碼物件吧!

[ "Alexius dreams"     "to"     "become"] "a" "DJ" "."
[  (Sub. + Verb)   (identifier)  (verb) ] ACTION: SHIFT

在 “become” 這個字開始往前看,我們看到了 “to” 這個字,這符合了英文語法裡的不定詞型態。不定詞具有代表著未來的規劃的意思,例如:I plan to do sth in the future. (我計畫著在未來要做某事) 所以我們可以開始簡化這個 “to become” 的組合變成一個名為 Infinitive (不定詞)語法丟到語法堆疊裡。

[ "Alexius dreams" "to become"  ] "a" "DJ" "."
[  (Sub. + Verb)   (Infinitive) ] ACTION: REDUCE

再一次往前看,我們再度發現又可以根據這個文法主詞 + 動詞 以及 不定詞 的語法組合起來,變成一個完整個句子結構。因此,這個簡化的動作又再度被觸發。

[ "Alexius dreams to become" ] "a" "DJ" "."
[ (Sub. + Verb + Infinitive) ] ACTION: REDUCE

然而,這個句子架構還沒完成,我們還是得移轉下一個符碼物件進去,這個時候的物件為 “a”,其在英文語法的詞性為冠詞(Article)。

[ "Alexius dreams to become"    "a"    ] "DJ" "."
[ (Sub. + Verb + Infinitive) (article) ] ACTION: SHIFT

由於冠詞無法單獨地存在,因此我們沒辦法和前面的句構合併,我們直接移轉下一個符碼物件。

[ "Alexius dreams to become"    "a"     "DJ"  ] "."
[ (Sub. + Verb + Infinitive) (article) (noun) ] ACTION: SHIFT

然後我們就可以發現,往回看,“a DJ” 也符合文法結構,在這個情況之下,其為 indefinite article (不定冠詞)。

[ "Alexius dreams to become"        "a DJ"       ] "."
[ (Sub. + Verb + Infinitive) (indefinite article)] ACTION: REDUCE

大致上到了這一步,通常已經習慣記得往回看了吧~很明顯地我們又發現根據這個句法結構,我們可以根據英文語法進行簡化的動作。

[  "Alexius dreams to become a DJ" ] "."
[ (Sub. + Verb + Infinitive + Obj.)] ACTION: REDUCE

根據英文語法,我們知道 “a DJ” 和前面句子組合之後, “a DJ” 變成了受詞(Object),因為他的位置在整個句子的後方。因此,它又再度變成了一個完整的句法結構。終於到了最後,我們移轉最後的符碼物件。

[  "Alexius dreams to become a DJ"     "."   ]
[ (Sub. + Verb + Infinitive + Obj.) (period) ] ACTION: SHIFT

句號在這個句子裡面代表句子的終結,所以我們再進行一次簡化。

[   "Alexius dreams to become a DJ."    ]
[ (Sub. + Verb + Infinitive + Obj. + .) ] ACTION: REDUCE

最後,你可以根據英文文法配合 LALR 語法解析演算法統合出這個句子的結構。你可以總結出 LALR 做了這些事情:

  • 試著往回看並找出可能的語法組合
  • 由左至右地解析每個符碼物件
  • 解析器會試著不斷地使用 移轉 — 簡化 的動作並推論出結果

AST 抽象語法樹

解析器不僅會幫你解析出結果,並且它會建立一個名為抽象語法樹的資料結構。舉一個簡單的例子,將一個簡單的表達式如下解析出它的語法樹。

a += 1 - 2 * (3 + 4)

我們可以改寫為以下形式。

a = a + 1 - 2 * (3 + 4)

表達式的裡面,括弧的優先權最高,其次是乘法或除法運算,再來是加法或減法運算。最後,指派式才是最後面的順序。

下面這張圖清楚地將語法樹的結果給解析出來。

I took a lot effort on visualizing this process, somehow it looks quite harmonic XD  —  Created by Jun-Xin Huang (Maxwell)

當抽象語法樹的結構已經建立完畢,我們就可以逛過整個語法樹並運算出整個表達式的結果!

總結!

在這段貢獻 Goby 專案的過程當中,我學到了很多的東西,甚至覺得自己擁有了各種有關於程式語言的強大知識!這個領域真的很酷而且我從來不知道這方面的知識。從符碼化的過程到解析整體語法,以及簡單的抽象語法樹的介紹,除此之外,當然還有更多不同的知識在貢獻這方面的領域過程當中學習到,包含 VM 的概念以及 Byte code 的解析等等 …

這些知識都有包含在名為 Ruby Under a Microscope 這本書裡面。它將會為你展示 Ruby 的程式碼是如何被解析的,Goby 也相對地使用了類似的方式,使用 Go 語言來建立出快要 100% 相容於 Ruby 語法的程式語言。

Ruby Under a Microscope

再度感謝 Goby 的創作者  —  Stan Lo,帶領我經歷這一趟精彩的旅程(雖然我還有很多有關於編譯器的概念還需要去學習),然後也感謝其他一起貢獻這個專案的開發者們,使得這個專案越來越好。我從來沒有想過我的第一次 開源貢獻的經驗會是進入一個神秘的領域然後成就了不少!


推薦文章:使用 JavaScript 以及 jQuery 打造旋轉式畫廊 Add Comment