在 Julia 當中實現 Cirru 解釋器的初步想法

文章內容所描述的方案, 已經實現了一個原型, 有時間我繼續改進
https://github.com/Cirru/CirruSepal.jlgit


昨天晚上不知怎麼想起來 Julia, 翻了翻文檔, 又有發現,
就是 Julia 有出色的元編程能力, 能夠在執行過程當中拼接 AST 而後執行
http://julia.readthedocs.org/en/latest/manual/metaprogramming/
好比說文檔裏給出了這樣一些例子:github

textjulia> ex2 = Expr(:call, :+, 1, 1)
:(1 + 1)

元編程

大體梳理一下, 我如今瞭解到的就是幾個方面,編程

  • Symbol 類型

Symbol 是和 String 不同的數據類型, 二者共同點是數據不可修改
二者差異須要看網上的說明, 可是 Symbol 是能夠用於生成 AST 的:
http://stackoverflow.com/questions/23480722/what-is-a-symbol-in-julia/...
或者說 Symbol 能夠用於表示程序自身, 好比說這樣的代碼:數組

textjulia> a = 1
1

julia> eval(a)
1

julia> eval(:a)
1
  • Quoting

Quote 能夠獲得表達式的 expr 對象, 對應表達式的 AST.
這些跟 Lisp 當中的 Quote 跟 Symbol 大概很是類似吧
只不過在 Julia 同時又有語法糖, 因此看起來怪怪的函數

textjulia> a = Expr(:call, :+, 1, 2)
:(1 + 2)

julia> b = :(1 + 2)
:(1 + 2)

julia> a == b
true

julia> eval(a)
3

julia> eval(b)
3
  • parse 函數

parse 能夠將一段字符串解析成 AST, 或者也能看出了就是 Quote:性能

textjulia> a = :(1 + 2)
:(1 + 2)

julia> c = parse("1 + 2")
:(1 + 2)

julia> a == c
true

而後能夠打印具體的結構, 用文檔例子裏的 dump 函數:優化

julia> dump(c)
Expr
  head: Symbol call
  args: Array(Any,(3,))
    1: Symbol +
    2: Int64 1
    3: Int64 2
  typ: Any

以及經過 eval 能夠將上邊獲得的 AST 執行:
http://julia.readthedocs.org/en/latest/stdlib/base/#Base.evalui

julia> eval(c)
3

思路

把上述幾個流程連接到一塊兒, 就也辦法在 Julia 當中本身生成 AST 去執行code

先查看正常的代碼如何編寫, 瞭解 AST 的結構:orm

julia> a = parse("1 + 2")
:(1 + 2)

julia> b = dump(a)
Expr
  head: Symbol call
  args: Array(Any,(3,))
    1: Symbol +
    2: Int64 1
    3: Int64 2
  typ: Any

而後用 Expr 本身構造一份 AST, 執行一下:

julia> c = Expr(:call, :+,  1, 2)
:(1 + 2)

julia> d = eval(c)
3

並且能夠下有個 Julia 自身實現的 parser, 其中有很多 AST 的例子
https://github.com/jakebolewski/JuliaParser.jl/blob/master/src/parser....

另外按照元編程文檔開頭講的, Julia 的 AST 跟 Lisp 很像, 都是表達式:

Like Lisp, Julia represents its own code as a data structure of the language itself. Since code is represented by objects that can be created and manipulated from within the language, it is possible for a program to transform and generate its own code. This allows sophisticated code generation without extra build steps, and also allows true Lisp-style macros operating at the level of abstract syntax trees.

就是說不像是 JavaScript AST 存在 Statement 那樣奇葩的結構,
而是在一層包裹下, 一切都是表達式, 可以實現自由組合, 看看分號分隔的語句:

julia> a = parse("1 + 2; 2 + 3")
:($(Expr(:toplevel, :(1 + 2), :(2 + 3))))

julia> dump(a)
Expr
  head: Symbol toplevel
  args: Array(Any,(2,))
    1: Expr
      head: Symbol call
      args: Array(Any,(3,))
        1: Symbol +
        2: Int64 1
        3: Int64 2
      typ: Any
    2: Expr
      head: Symbol call
      args: Array(Any,(3,))
        1: Symbol +
        2: Int64 2
        3: Int64 3
      typ: Any
  typ: Any

整體上是一個嵌套表達式的結構, 也就是 Cirru 所模仿的 Lisp 的形態
所以 Cirru 的語法是個 Julia 的 AST 結構對應的,
就有可能達到編寫 Cirru 腳本, 直接替換爲 Julia AST 執行

前面已經說過, Julia 能夠在運行過程當中生成 LLVM IR 的
就是說實現的話, Cirru 的解釋器就算跑在 LLVM IR 上邊了
不知道性能如何, 可是有 Julia 作中間層優化, 效果呢應該不會太差
以前 Sepal 項目直接放棄了, 這個地方卻是有一線但願

另外一個之後須要考慮的是多個文件組成模塊系統的問題,
如今能夠先不考慮的吧..

實現問題

Cirru 在 JavaScript 當中是先轉化成 JSON 對象, 而後操做的
Julia 固然少不了操做 JSON 的庫, 我最初的思路是從 JSON 開始
https://github.com/JuliaLang/JSON.jl

不過這地方有棘手的問題, Cirru 的 JSON 結構是嵌套的數組,
而 Jualia 當中, 分紅了不一樣的 Array 跟 Tuple 兩種結構,
http://en.wikibooks.org/wiki/Introducing_Julia/Arrays_and_tuples
Array 限定了元素類型一致, 能夠不斷增加, 而 Tuple 容許多種類型, 但不能增加
這個相似 Haskell, Haskell 的 Cirru Parser 用代數類型系統解決了,
由於就是說 Haskell 定義類型系統支持遞歸, 就能表示 Cirru 的樹
可是 Julia 我彷佛沒找到遞歸類型.. 那麼 JSON 還能用麼?
極端畢竟注重性能的語言, 很難像 JavaScript 跟 Haskell 這麼瀟灑...

另外我也嘗試考慮直接解析 Cirru 代碼生成 Quote 的可能,
目前 Cirru 解析分紅兩步,

  1. 生成基於縮進跟括號的樹, 其中尚未出來 $, 造成的轉換
  2. 識別 $, 對樹進行 desuger 的轉換

原來 parser 的實現極大地藉助了 JSON 自由的結構,
這裏直接生成 Quote 彷佛也是不可行的, 中間過程仍是受到 Array 和 Tuple 限制.

其餘的考慮, 還有其餘一些 Lisp LLVM 實現, 我在 Google 隨手找到的:
https://github.com/drmeister/clasp
https://github.com/artagnon/rhine-ml
https://github.com/mylesmegyesi/lisp-compiler-llvm
https://github.com/eudoxia0/corvus
通常 Lisp 會有直接操做 AST 的功能, 也就是 Quote
至少從原理上, Cirru 轉化成 Quote, 而後 eval, 這是行得通的
但考慮到我對 Lisp 自己不夠深刻, 短期是無法看了
不過, 從成熟度上, 我想要 Cirru 生成 LLVM IR 玩, 仍是 Julia 吧..

總之就是還沒找到具體實現的辦法.


看了份中文的筆記, 發現能夠定義任意類型的數組... 得研究下
http://www.justinablog.com/archives/1604

julia> {"1", {"2", "3", {"4"}}}
2-element Array{Any,1}:
 "1"
 {"2","3",{"4"}}
相關文章
相關標籤/搜索