Boo(4) – booi 與 booish

Boo 有兩個很方便的工具:booi 與 booish
booi 可以用來直接執行你寫好的 script,而不必事先編譯。
而 booish 則可以讓你直接輸入指令,立即就能看到結果。
類比其他語言的話,booi 用法就像是執行 wscript your_script 或是 python your_script:booi your_script。
booish 就像是單獨執行 Pythonruby 是一樣的,執行以後,會出現訊息以及提示符號。
booish 的提示符號是 >>> ,與 Python 一樣,使用方法也很類似。
使用 booish 的時候,我發現很詭異的地方,就是使用 Mono 包進來的 booish 與 ipy (IronPython) 時,輸入任何字元,都會自動出現兩次以上…
boo 官方網站所提供的 booish 就沒有這個問題,這讓我很納悶。
有時間來找找這問題發生的原因。

Boo(3) – ildasm

使用 ildasm 有兩個目的:

  1. 比較print macro 與 print 函數的差別
  2. 比較 booc 編譯出來的可執行檔與 c# 編譯出來的可執行代碼

print macro 與 print 函數算是差別很大吧。
print 函數會調用 Boo.Lang.Builtins 類別裡的 print 函數,雖然實際上此函數的內容也是使用 Console.WriteLine(),但是除了你需要多附上 Boo.Lang.dll 之外,你還需要負擔 CLR 執行時動態把 Boo.Lang.dll 載入的成本。

IL_0005: call void [Boo.Lang/*23000001*/]Boo.Lang.Builtins/*01000001*/::print(object) /* 0A000001 */

使用 print macro 的話,就只是把 Console.WriteLine 替換進去,以編譯出來的結果而言,這會比較有效率,但如果作為 script 執行時,我想應該會有些損失。

IL_0005: call void [mscorlib/*23000001*/]System.Console/*01000001*/::WriteLine(string) /* 0A000001 */

這就是為甚麼BOO Primer建議使用 print macro 的原因。
booc 編譯出來的結果與使用 gmcs (我使用 Mono 的 c# 2.0 編譯器)編譯出來的結果非常接近,都同樣直接呼叫 System.Console.WriteLine,可以視為一樣。
p.s. 我也看過以 csc 編譯出來的結果了,實際上也非常相近,不過反組譯出來的IL代碼裡面的編排有些許不同。

BOO(2) – Hello world!

接下來的系列文章,大致會照著BOO Primer的章節來作介紹。

是的,永遠的 Hello world!

BOO 的 Hello world! 非常簡單:

// 把 print 當 macro 使用,什麼是 macro ??日後再說明
print "Hello world!"
// 或者是拿 print 當函數使用
print("Hello world!")

官方建議使用 print macro 的版本,我想是效率上的考量,macro 在執行時會展開為真正的代碼,而不是以函數呼叫的方式來處理。不過我覺得這見仁見智,差別不是很大。另一個好處應該是很像 Python 吧~

BOO (1)

BOO是一個兼具動態語言特性與靜態語言特性的語言,比IronPythonIronRuby還要早誕生,但是使用的人卻不多,我對他會有興趣的原因是因為他很接近 Python,而且可以編譯為 exe/dll、定義出屬於自己的 Domain-specific language(使用 macro)。
Q:哪裡可以取得?
A:你可以到 BOO 官方下載網頁 下載。
Q:如何安裝?
A:解開下載來的壓縮檔即可,除此之外,你還需要安裝 .Net framework 2,這可以用 Windows Update 裝起來。此外,你也可以透過安裝Mono或是SharpDevelop 2.x的方式來取得。
Q:跟其他 .Net 語言,如 c#, vb.net 等,有什麼不一樣?
A:最大的不同點在於BOO可以當作 script 來使用,你可以使用 booi 直接執行程式,或是像Python一樣,當作 SHELL 來使用 (booish)。
Q:如何使用?
A:

  • 當作 shell 來使用:解開之後,你可以在 bin 目錄下找到 booish.exe,點選以後執行。
  • 執行 script:在命令提示字元下切換目錄到 boo_path/bin 以後,輸入 booi your_script.boo
  • 編譯為 dll/exe:在命令提示字元下切換目錄到 boo_path/bin 以後,輸入 booc -target:[exe|library|winexe] -o:輸入檔名 your_script.boo

Q:有哪些文件可以參考?
A:官方網站的 Tutorials 列出了相當多的文件,我自己主要是參考Boo Primer

Boo hack(2)

回頭看 Boo.Lang.Compiler.CompilerParameters (就是前面提到的 _parser.Parameters),發現裡面做的事情並不是放參數那麼簡單。
除了 Input 之外,他還負責初始化必要的東西,例如載入預設的 Assembly:mscorlib、System、Boo.Lang.Builtins、Boo.Lang.Compiler~
回到正題,BooCompiler.Run() (前面提到的_parser) 起始 CompilerContext之後,再把 context 傳給 CompilerParameters.Pipeline.Run() 去執行。
_parser.Parameters.Pipeline 早在 AbstractInterpreter 時,就已經初始:Pipelines.Parse.NewParserStep()。
p.s.

Boo hack(1)

從 booish 開始,我想會比較快吧~
這個互動的 shell,你可以像 BASIC 一樣,打指令以後,立刻就可以看到結果。
booish 本身是一個很簡單的小程式,產生 InteractiveInterpreter2(在Boo.Lang.Interpreter下) 的 Instance 以後,調用 ConsoleLoopEval() 來等待使用者輸入程式並執行。
等使用者按下 enter 的時候,就試著呼叫 TryRunCommand() 看是不是內建的指令。
不是的話,就調用 InternalLoopEval(),而 InternalLoopEval() 則是呼叫父類別:AbstractInterpreter.Eval()。
AbstractInterpreter.Eval() 簡單的把字串參數轉換為 StringInput (繼承 ReaderInput,ReaderInput再繼承 ICompilerUnit),再讓 EvalCompilerInput() 去呼叫 Parse()。 (註1)
Parse() 則是先把 _parser 的輸入參數清空,把 ICompilerUnit 參數放到 _parser 的輸入參數(Parameters.Input)裡面,執行 _parser.Run()。
_parser 的型別是 BooCompiler,至此,要開始看 Boo.Lang.Compiler.BooCompiler 了。
所以,看起來 Boo.Lang.Interpreter 是用 Boo.Lang.Compiler 命名空間裡的類別在做事。
註:

  1. 想不到可以這樣用:

    return EvalCompilerInput(StringInput(“input${++_inputId}”, code))

    ,Boo 可以讓變數直接帶入字串,如: “${_inputId}”,但我不知道裡面還可以作運算,如:”${++_inputId}”