當前位置: 華文星空 > 知識

你寫過什麽有趣的程式?

2016-10-08知識

本科期間發明了個程式語言,和小夥伴一起寫了個編譯器。

世上現有的程式語言都太破了。
我要自己設計個語言,做出編譯器。
來證明還可以有更破的。

Shoolang是程序導向的、靜態作用域的、強類別的通用程式語言。通俗講,就是有點像C/C++,但是少了很多的功能,以及多了很少的功能。

Shoo支持多維陣列、遞迴、巢狀函數,可以寫出大部份的基礎演算法。比如氣泡排序,用頭等函數(first class functions)自訂比較方式,來把一堆structs排序:

Shoolang氣泡排序展示

還能拿去砍Leetcode題,比如解數獨:

Leetcode #37 Sudoku Solver

按照使用者輸入,畫個字元畫什麽的:

Shoolang生成字元畫

我們有17頁的語言手冊【Shoo語言:從入門到放棄】:

Shoolang語言手冊截圖

https:// github.com/sam-jay/shoo -lang/blob/master/Language_Reference_Manual.md

作為史上最破,Shoo編譯器有以下特性:

  • 垃圾回收全靠重新開機。遇到問題,重新開機試試。
  • 不支持互相遞迴,所以寫不了Lisp直譯器。
  • 不能鏈多個檔,所有源碼要寫在一個檔裏。
  • Shoo編譯器由以下幾個模組串在一起——

    1)詞法分析(scanner.mll)

    將讀入的ASCII碼形式的原始碼轉化成一個個token,比如關鍵詞、數碼、字串、運算子、括弧等等。程式碼的註釋會在這一步被舍棄。

    2)語法分析(parser.mly)

    把上一步產生的token,根據定義好的Shoo語法,轉換成一個抽象語意樹(abstract syntax tree,簡稱AST)。語法糖也在這一步轉化。

    3)語意分析(sement.ml)

    遍歷AST樹,將其轉換為SAST樹。這個樹的每個節點都是一個包含了多種後設資料的物件(object),在後一步驟有用。

    理論上不檢查原始碼是否符合要求也是可以的,我們可以寫一個很暴躁的編譯器,啥也不檢查,二話不說轉成二進制碼,跑崩了誰寫的碼誰背鍋。為了世界的和平,還是好好地檢查錯誤了。

    4)中間程式碼的轉換(lift.ml)

    Shoolang支持頭等函數(first class functions),所以有對應的模組負責做轉換。其它可能的模組有類別推斷(type inference),那麽再串一個模組做類別的轉換。

    Shoo編譯器用SAST節點的後設資料記錄語境資訊,將所有被巢狀的函數上升到全域範圍的同時不改變程式表現。每個語境包含獨有的變量,用關聯地圖(map)儲存。

    5)程式碼生成(codegen.ml)

    直接對接LLVM模組,把SAST節點轉成更低階的語言。這種方法的前提是,Shoo語言的原始碼和LLVM程式碼存在token層級的一一對應關系。舉個反例,「or」這個運算子在有的語言中有短路效果,於是我們舍棄了短路這個特性。

    然後連結C庫(builtins.c),用來輔助完成輸入輸出的一些功能,還有字串相關的操作。最後輸出二進制碼。

    關於用OCaml寫編譯器。OCaml定義上下文無關文法(context free grammar)方便得跟開掛一樣,還可以各種調Lex和LLVM模組。

    用Ocaml寫也有缺點。很難學。超級煩。但是很有用。你會花很多很多的時間,寫很少很少的碼,來做很多很多事情。

    最後,造編譯器推薦看龍書(Compilers: Principles, Techniques, and Tools by Alfred V. Aho, Ravi Sethi, Jeffrey D.Ullman)。裏面啥都有,除了龍。

    ==============================================

    微博@甜菜欣欣,定期更新寫碼日常;

    公主號「甜菜欣欣」,非週期性分享寫碼留學找工tips~

    相關回答: