June 16,2008
Boo(19)-例外處理
例外處理的語法與 Python 相近,差別在於 Boo 使用 ensure,而 Python 使用 finally。
除此之外,Boo 統一使用 except 處理各種例外,而 Python 使用 else 處理無法處理的例外型態。
除此之外,Boo 統一使用 except 處理各種例外,而 Python 使用 else 處理無法處理的例外型態。
import System
class MyException(Exception):
_msg as string
def constructor( s as string ):
_msg = s
override def ToString() as string:
return "MyException::${_msg}"
// 試著調整這兩個變數試試看
isExceptionHappen = false
isMyExceptionHappen = true
try:
// .. do something ...
if isExceptionHappen:
raise Exception("Something wrong.") // 提出例外情況
// ...
if isMyExceptionHappen:
raise MyException("Hey!!")
except e as MyException:
print e.ToString()
except e as Exception:
print e.Message
ensure:
print "不管有沒有錯誤,這裡都會被執行。"
參考:Boo Primer - 例外、Python tutorial - 8. Errors and Exceptions
June 13,2008
Boo(18)-命名空間
.NET上的語言幾乎都導入命名空間了,Boo 無法置身事外...
命名的方式,則是在原始檔第一行加上: namespace 命名空間名稱
撇開註解不算,命名空間的宣告,無論如何都要是程式碼的第一行,否則會有錯誤發生。
引用時,則是使用 import 關鍵字,例如:
對了,組件不需要特別加上 ".dll"
命名的方式,則是在原始檔第一行加上: namespace 命名空間名稱
撇開註解不算,命名空間的宣告,無論如何都要是程式碼的第一行,否則會有錯誤發生。
引用時,則是使用 import 關鍵字,例如:
import System Console.WriteLine( "Hello world!" ) // 為甚麼要引用命名空間?因為這樣寫很累... System.Console.WriteLine( "Hello again." )你也可以指明組件(Assembly)的名稱,所以這幾種寫法也行:
import System.Data from System.Data import Gtk from "gtk-sharp"
對了,組件不需要特別加上 ".dll"
June 6,2008
Boo(17)-結構與列舉
結構(struct)跟類別很類似,最明顯的差別在於 class 被換成 struct 了,類別的一些特性也可以在結構上使用。
其他的差別:無法繼承類別、結構,只能實作 Interface﹔結構是值型別,在複製實體時,是整個克隆(Clone)而不是像類別一樣,只做參考。
struct Dog:
def constructor( name ):
_name=name
[property(Name)]
_name as string
emptydog=Dog()
print "emptydog.Name=${emptydog.Name}" // 什麼都沒印出
lucky=Dog("Lucky")
print "lucky.Name=${lucky.Name}" // 印出 Lucky
列舉(enum),如果你有用過 C/C++/C# 的話,應該不陌生:
// 宣告列舉
enum Day:
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Sunday
// 也可以指定數值
enum Task:
TODO=100
FIXME=101
// 列印
print Day.Sunday
// 尋訪列舉型別裡所有元素
for s in Enum.GetNames(Day):
print s
// 另一種
for n,v in array( zip( Enum.GetNames(Task), Enum.GetValues(Task)) ):
print "${n}=${v}"
June 2,2008
Boo(16)-Class
Boo 的類別(Class),跟 Python 很像,基本上不複雜。
class 跟 C# 一樣,可以加上 public、protected、internal、protected internal、private、abstract、final 等修飾詞,預設是 public。
繼承的話,就是在類別名稱後面加上小括號,並在括號內放置欲繼承的類別。
建構子與解構子分別是 constructor 與 destructor,可寫可不寫。
方法的宣告其實跟前面提到的函數很像,都是使用 def ,def 的前面還可以加上 abstract、static、virtual、override 等修飾詞。
最後是欄位,通常就跟寫運算式一樣,給定一個值就行了,像這樣:_name="",前面的 [property()] 是 attribute,是一個偷懶的寫法,實際上是 get/set 的組合體:
看到這裡,你有發現到這行嗎?whity=Dog( Name:"whity" )。咦,莫非在建構時可以直接指定屬性的值,沒錯,這寫起程式來方便很多啊~
參考資料:
class Animal:
pass
class Dog(Animal):
def constructor():
pass
def constructor( name ):
_name=name
def destructor():
pass
def Bark():
print "${_name} is barking..."
[property(Name)]
_name = "Anonymous"
spot=Dog( "spot" )
whity=Dog( Name:"whity" )
print spot.Name
whity.Bark()
class 跟 C# 一樣,可以加上 public、protected、internal、protected internal、private、abstract、final 等修飾詞,預設是 public。
繼承的話,就是在類別名稱後面加上小括號,並在括號內放置欲繼承的類別。
建構子與解構子分別是 constructor 與 destructor,可寫可不寫。
方法的宣告其實跟前面提到的函數很像,都是使用 def ,def 的前面還可以加上 abstract、static、virtual、override 等修飾詞。
最後是欄位,通常就跟寫運算式一樣,給定一個值就行了,像這樣:_name="",前面的 [property()] 是 attribute,是一個偷懶的寫法,實際上是 get/set 的組合體:
class Cat(Animal):
def constructor():
pass
def constructor( name ):
_name=name
def destructor():
pass
def Meow():
print "${_name} is meowing..."
Name as string:
get:
return _name
set:
_name=value
_name = "Anonymous"
看到這裡,你有發現到這行嗎?whity=Dog( Name:"whity" )。咦,莫非在建構時可以直接指定屬性的值,沒錯,這寫起程式來方便很多啊~
參考資料:
May 29,2008
Boo(15)-內建函數:容器操作
join()、map()、array()、matrix()、iterator()、enumerate()、range()、reversed()、zip()、cat()
這一類的函式還...蠻多的,大多都與 python 相容。
join(),把 Enumerator 裡面每個元素轉成字串,最後串成一個字串傳回。你也可以加上第二個引數,他會自動幫你加上,例如:join( [1,2,3,4,5], ":" ) 會得到 "1:2:3:4:5" 的字串。
map(),對 Enumerator 裡面每個元素施行指定的函式。
array(),傳入一個 Enumerator 回傳一個陣列。
matrix(),建立多維陣列。
iterator(),取得物件的 IEnumerable 介面,如果物件沒有 IEnumerable 介面,但有繼承 TextReader 的話,則改用 TextReaderEnumerator.lines() 取得 IEnumerable。這個函數在內部非常頻繁地被這裡提到的其他函數使用到。
enumerate(),先取得物件的 IEnumerable 介面,然後傳回類似 (index, value ) 的 Enumerator,舉例來說,List( enumerate( [ "a", "b", "c", "d" ] ) ) 的結果會是:[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]。
range() 很容易理解,傳入數值,會回傳有循序數值的 Enumerator,你也可以傳入起始與結束的數值或是傳入起始、結束與遞增數。
reversed(),將 Enumerator 裡面的元素以相反順序擺放,內部是使用 ReversedListEnumerator 類別來完成這件事情。
zip(),傳入多個 Enumerator,它會把每個 Enumerator 的第 0 個元素放到一起、第 1 個元素放到一起...以此類推,最後再傳回一個 Enumerator。這個函數看例子會比較容易了解,array(zip(['a','b','c'],[4,5,6],['aa','bb','cc'])) 的結果會是 (('a', 4, 'aa'), ('b', 5, 'bb'), ('c', 6, 'cc'))。老實說,我還沒想到要怎麼用...
cat(),跟 join 有點像,不過不會傳回字串,而是把傳入的 Enumerator 串接起來成一個 Enumerator 再傳回。
這裡有的函數我沒舉例,要看例子的話,可以參考Boo Primer中文版對內建函數的說明。
這一類的函式還...蠻多的,大多都與 python 相容。
join(),把 Enumerator 裡面每個元素轉成字串,最後串成一個字串傳回。你也可以加上第二個引數,他會自動幫你加上,例如:join( [1,2,3,4,5], ":" ) 會得到 "1:2:3:4:5" 的字串。
map(),對 Enumerator 裡面每個元素施行指定的函式。
array(),傳入一個 Enumerator 回傳一個陣列。
matrix(),建立多維陣列。
iterator(),取得物件的 IEnumerable 介面,如果物件沒有 IEnumerable 介面,但有繼承 TextReader 的話,則改用 TextReaderEnumerator.lines() 取得 IEnumerable。這個函數在內部非常頻繁地被這裡提到的其他函數使用到。
enumerate(),先取得物件的 IEnumerable 介面,然後傳回類似 (index, value ) 的 Enumerator,舉例來說,List( enumerate( [ "a", "b", "c", "d" ] ) ) 的結果會是:[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]。
range() 很容易理解,傳入數值,會回傳有循序數值的 Enumerator,你也可以傳入起始與結束的數值或是傳入起始、結束與遞增數。
reversed(),將 Enumerator 裡面的元素以相反順序擺放,內部是使用 ReversedListEnumerator 類別來完成這件事情。
zip(),傳入多個 Enumerator,它會把每個 Enumerator 的第 0 個元素放到一起、第 1 個元素放到一起...以此類推,最後再傳回一個 Enumerator。這個函數看例子會比較容易了解,array(zip(['a','b','c'],[4,5,6],['aa','bb','cc'])) 的結果會是 (('a', 4, 'aa'), ('b', 5, 'bb'), ('c', 6, 'cc'))。老實說,我還沒想到要怎麼用...
cat(),跟 join 有點像,不過不會傳回字串,而是把傳入的 Enumerator 串接起來成一個 Enumerator 再傳回。
這裡有的函數我沒舉例,要看例子的話,可以參考Boo Primer中文版對內建函數的說明。
May 23,2008
Boo(14)-內建函數:輸入與輸入
print、gets、prompt
print 就是調用 Console.WriteLine() 而已,官方建議使用 print macro,而不要使用這個函數。
gets 從標準輸入取得一個字串,實際上就是調用 Console.ReadLine()。
prompt 是 Console.ReadLine() + Console.Write() 的組合技,在印出你給的提示訊息之後,會接著從標準輸入取得字串。
從標準輸入取得字串的意思就是,畫面會停住,等你輸入字元,直到你按下 Enter 之後,才把你輸入的字元放到字串裡傳回。
當然,除了這些函數以外,你還是可以直接使用 .NET Framework 裡的 System.IO 來處理。
print 就是調用 Console.WriteLine() 而已,官方建議使用 print macro,而不要使用這個函數。
gets 從標準輸入取得一個字串,實際上就是調用 Console.ReadLine()。
prompt 是 Console.ReadLine() + Console.Write() 的組合技,在印出你給的提示訊息之後,會接著從標準輸入取得字串。
從標準輸入取得字串的意思就是,畫面會停住,等你輸入字元,直到你按下 Enter 之後,才把你輸入的字元放到字串裡傳回。
print("Hello")
s = gets()
print s
s = prompt("Please input something:")
print s
當然,除了這些函數以外,你還是可以直接使用 .NET Framework 裡的 System.IO 來處理。
May 21,2008
Boo(13)-內建函數:shell 類
shell()、shellp()、shellm()
顧名思義,就是執行外部的程式。
shell() 會等待外部程式執行完成以後,回傳一個字串,字串裡是執行的結果。
shellp() 不會等待外部程式執行完成,會直接回傳 Process 物件,事實上,shell() 也呼叫了這個函數,只是 shell() 拿到 Process 物件以後,利用 Process.StandardOutput() 去讀取執行結果,並使用 Process.WaitForExit() 等待程序執行完成。
shellm() 也是執行外部程式,但這個外部程式必須是 Managed,也就是 .NET 應用程式。老實說,看了 boo 源碼以後,我不是很懂。源碼裡面是建立一個新的 AppDomain,載入指定的程式,然後找到 EntryPoint 並執行。我猜想,這樣的作法主要用來避免再次建立新程序、啟動 CLR,在 CPU、記憶體使用上會比較有效率。如果你的外部程式正好也是 .NET 應用程式的話,就用 shellm(),我想會比較好。
顧名思義,就是執行外部的程式。
shell() 會等待外部程式執行完成以後,回傳一個字串,字串裡是執行的結果。
shellp() 不會等待外部程式執行完成,會直接回傳 Process 物件,事實上,shell() 也呼叫了這個函數,只是 shell() 拿到 Process 物件以後,利用 Process.StandardOutput() 去讀取執行結果,並使用 Process.WaitForExit() 等待程序執行完成。
shellm() 也是執行外部程式,但這個外部程式必須是 Managed,也就是 .NET 應用程式。老實說,看了 boo 源碼以後,我不是很懂。源碼裡面是建立一個新的 AppDomain,載入指定的程式,然後找到 EntryPoint 並執行。我猜想,這樣的作法主要用來避免再次建立新程序、啟動 CLR,在 CPU、記憶體使用上會比較有效率。如果你的外部程式正好也是 .NET 應用程式的話,就用 shellm(),我想會比較好。
input = shell( "booc.exe", "" )
May 19,2008
booc 的 49 道工法
從 Visual Studio debugger 裡面截出來的...想不到編譯需要這麼多步驟...
第 0 步由 Boo.Lang.Compiler.Pipelines.Parse (src\Boo.Lang.Compiler\Pipelines\Parse.cs) 加入。
第 1~27 步由 Boo.Lang.Compiler.Pipelines.ResolveExpressions (src\Boo.Lang.Compiler\Pipelines\ResolveExpressions.cs) 加入。
第 28~46 步由 Boo.Lang.Compiler.Pipelines.Compile (src\Boo.Lang.Compiler\Pipelines\Compile.cs)加入。
第 47 步由 Boo.Lang.Compiler.Pipelines.CompileToMemory (src\Boo.Lang.Compiler\Pipelines\CompileToMemory.cs) 加入。
第 48 步由 Boo.Lang.Compiler.Pipelines.CompileToFile (src\Boo.Lang.Compiler\Pipelines\CompileToFile.cs)加入。
這些步驟都是利用繼承的關係建立起來的:CompileToFile -> CompileToMemory -> Compile -> ResolveExpressions -> Parse
只應用了繼承的威力...
- _items {維度:[64]} object[]
+ [0] {Boo.Lang.Parser.BooParsingStep} object {Boo.Lang.Parser.BooParsingStep}
+ [1] {Boo.Lang.Compiler.Steps.InitializeTypeSystemServices} object {Boo.Lang.Compiler.Steps.InitializeTypeSystemServices}
+ [2] {Boo.Lang.Compiler.Steps.PreErrorChecking} object {Boo.Lang.Compiler.Steps.PreErrorChecking}
+ [3] {Boo.Lang.Compiler.Steps.ExpandAstLiterals} object {Boo.Lang.Compiler.Steps.ExpandAstLiterals}
+ [4] {Boo.Lang.Compiler.Steps.MergePartialClasses} object {Boo.Lang.Compiler.Steps.MergePartialClasses}
+ [5] {Boo.Lang.Compiler.Steps.InitializeNameResolutionService} object {Boo.Lang.Compiler.Steps.InitializeNameResolutionService}
+ [6] {Boo.Lang.Compiler.Steps.IntroduceGlobalNamespaces} object {Boo.Lang.Compiler.Steps.IntroduceGlobalNamespaces}
+ [7] {Boo.Lang.Compiler.Steps.TransformCallableDefinitions} object {Boo.Lang.Compiler.Steps.TransformCallableDefinitions}
+ [8] {Boo.Lang.Compiler.Steps.BindTypeDefinitions} object {Boo.Lang.Compiler.Steps.BindTypeDefinitions}
+ [9] {Boo.Lang.Compiler.Steps.BindGenericParameters} object {Boo.Lang.Compiler.Steps.BindGenericParameters}
+ [10] {Boo.Lang.Compiler.Steps.BindNamespaces} object {Boo.Lang.Compiler.Steps.BindNamespaces}
+ [11] {Boo.Lang.Compiler.Steps.BindBaseTypes} object {Boo.Lang.Compiler.Steps.BindBaseTypes}
+ [12] {Boo.Lang.Compiler.Steps.BindAndApplyAttributes} object {Boo.Lang.Compiler.Steps.BindAndApplyAttributes}
+ [13] {Boo.Lang.Compiler.Steps.ExpandMacros} object {Boo.Lang.Compiler.Steps.ExpandMacros}
+ [14] {Boo.Lang.Compiler.Steps.IntroduceModuleClasses} object {Boo.Lang.Compiler.Steps.IntroduceModuleClasses}
+ [15] {Boo.Lang.Compiler.Steps.NormalizeStatementModifiers} object {Boo.Lang.Compiler.Steps.NormalizeStatementModifiers}
+ [16] {Boo.Lang.Compiler.Steps.NormalizeTypeAndMemberDefinitions} object {Boo.Lang.Compiler.Steps.NormalizeTypeAndMemberDefinitions}
+ [17] {Boo.Lang.Compiler.Steps.BindTypeDefinitions} object {Boo.Lang.Compiler.Steps.BindTypeDefinitions}
+ [18] {Boo.Lang.Compiler.Steps.BindGenericParameters} object {Boo.Lang.Compiler.Steps.BindGenericParameters}
+ [19] {Boo.Lang.Compiler.Steps.BindEnumMembers} object {Boo.Lang.Compiler.Steps.BindEnumMembers}
+ [20] {Boo.Lang.Compiler.Steps.BindBaseTypes} object {Boo.Lang.Compiler.Steps.BindBaseTypes}
+ [21] {Boo.Lang.Compiler.Steps.BindMethods} object {Boo.Lang.Compiler.Steps.BindMethods}
+ [22] {Boo.Lang.Compiler.Steps.ResolveTypeReferences} object {Boo.Lang.Compiler.Steps.ResolveTypeReferences}
+ [23] {Boo.Lang.Compiler.Steps.BindTypeMembers} object {Boo.Lang.Compiler.Steps.BindTypeMembers}
+ [24] {Boo.Lang.Compiler.Steps.ProcessInheritedAbstractMembers} object {Boo.Lang.Compiler.Steps.ProcessInheritedAbstractMembers}
+ [25] {Boo.Lang.Compiler.Steps.CheckMemberNames} object {Boo.Lang.Compiler.Steps.CheckMemberNames}
+ [26] {Boo.Lang.Compiler.Steps.ProcessMethodBodiesWithDuckTyping} object {Boo.Lang.Compiler.Steps.ProcessMethodBodiesWithDuckTyping}
+ [27] {Boo.Lang.Compiler.Steps.PreProcessExtensionMethods} object {Boo.Lang.Compiler.Steps.PreProcessExtensionMethods}
+ [28] {Boo.Lang.Compiler.Steps.UnfoldConstants} object {Boo.Lang.Compiler.Steps.UnfoldConstants}
+ [29] {Boo.Lang.Compiler.Steps.OptimizeIterationStatements} object {Boo.Lang.Compiler.Steps.OptimizeIterationStatements}
+ [30] {Boo.Lang.Compiler.Steps.BranchChecking} object {Boo.Lang.Compiler.Steps.BranchChecking}
+ [31] {Boo.Lang.Compiler.Steps.CheckIdentifiers} object {Boo.Lang.Compiler.Steps.CheckIdentifiers}
+ [32] {Boo.Lang.Compiler.Steps.StricterErrorChecking} object {Boo.Lang.Compiler.Steps.StricterErrorChecking}
+ [33] {Boo.Lang.Compiler.Steps.CheckAttributesUsage} object {Boo.Lang.Compiler.Steps.CheckAttributesUsage}
+ [34] {Boo.Lang.Compiler.Steps.ExpandDuckTypedExpressions} object {Boo.Lang.Compiler.Steps.ExpandDuckTypedExpressions}
+ [35] {Boo.Lang.Compiler.Steps.ProcessAssignmentsToValueTypeMembers} object {Boo.Lang.Compiler.Steps.ProcessAssignmentsToValueTypeMembers}
+ [36] {Boo.Lang.Compiler.Steps.ExpandProperties} object {Boo.Lang.Compiler.Steps.ExpandProperties}
+ [37] {Boo.Lang.Compiler.Steps.RemoveDeadCode} object {Boo.Lang.Compiler.Steps.RemoveDeadCode}
+ [38] {Boo.Lang.Compiler.Steps.CheckMembersProtectionLevel} object {Boo.Lang.Compiler.Steps.CheckMembersProtectionLevel}
+ [39] {Boo.Lang.Compiler.Steps.NormalizeIterationStatements} object {Boo.Lang.Compiler.Steps.NormalizeIterationStatements}
+ [40] {Boo.Lang.Compiler.Steps.ProcessSharedLocals} object {Boo.Lang.Compiler.Steps.ProcessSharedLocals}
+ [41] {Boo.Lang.Compiler.Steps.ProcessClosures} object {Boo.Lang.Compiler.Steps.ProcessClosures}
+ [42] {Boo.Lang.Compiler.Steps.ProcessGenerators} object {Boo.Lang.Compiler.Steps.ProcessGenerators}
+ [43] {Boo.Lang.Compiler.Steps.ExpandVarArgsMethodInvocations} object {Boo.Lang.Compiler.Steps.ExpandVarArgsMethodInvocations}
+ [44] {Boo.Lang.Compiler.Steps.InjectCallableConversions} object {Boo.Lang.Compiler.Steps.InjectCallableConversions}
+ [45] {Boo.Lang.Compiler.Steps.ImplementICallableOnCallableDefinitions} object {Boo.Lang.Compiler.Steps.ImplementICallableOnCallableDefinitions}
+ [46] {Boo.Lang.Compiler.Steps.CheckNeverUsedMembers} object {Boo.Lang.Compiler.Steps.CheckNeverUsedMembers}
+ [47] {Boo.Lang.Compiler.Steps.EmitAssembly} object {Boo.Lang.Compiler.Steps.EmitAssembly}
+ [48] {Boo.Lang.Compiler.Steps.SaveAssembly} object {Boo.Lang.Compiler.Steps.SaveAssembly}
第 0 步由 Boo.Lang.Compiler.Pipelines.Parse (src\Boo.Lang.Compiler\Pipelines\Parse.cs) 加入。
第 1~27 步由 Boo.Lang.Compiler.Pipelines.ResolveExpressions (src\Boo.Lang.Compiler\Pipelines\ResolveExpressions.cs) 加入。
第 28~46 步由 Boo.Lang.Compiler.Pipelines.Compile (src\Boo.Lang.Compiler\Pipelines\Compile.cs)加入。
第 47 步由 Boo.Lang.Compiler.Pipelines.CompileToMemory (src\Boo.Lang.Compiler\Pipelines\CompileToMemory.cs) 加入。
第 48 步由 Boo.Lang.Compiler.Pipelines.CompileToFile (src\Boo.Lang.Compiler\Pipelines\CompileToFile.cs)加入。
這些步驟都是利用繼承的關係建立起來的:CompileToFile -> CompileToMemory -> Compile -> ResolveExpressions -> Parse
只應用了繼承的威力...
May 16,2008
Boo(12)-函數
函數定義方法很簡單,比較特別的就是不定個數變數。
然後有看到 Say() 定義了三次嗎?是的,Boo 支援多載(overloading)。
不定個數變數,定義的方法比較特別,要加上 *,然後用法就當作是 enumerator 來用就行了。
// Say
def Say( s as string):
print s
// 也是 Say
def Say( i as int):
print i
// 不定個數
def Say(*args as (object)):
print "len(args)=${len(args)}"
for arg in args:
print arg
// 求平方
def pow( i as int ) as int:
return i*i
Say( "Hello world!" )
Say( 20 )
Say( pow( 2 ) )
Say( 1, "s", join(range(10)) )
a = (5, 8, 1, "end")
Say(*a)
as string、as int...等,其實都可以省略不寫,別忘了 Boo 會自動判定。然後有看到 Say() 定義了三次嗎?是的,Boo 支援多載(overloading)。
不定個數變數,定義的方法比較特別,要加上 *,然後用法就當作是 enumerator 來用就行了。
May 14,2008
switch-case in boo
Boo本身並沒有類似 switch-case 語法,但是可以藉著 macro 來做到,Boo extensions這個專案已經寫好了。
由於這個專案沒有釋出二進位碼,所以你需要自己 checkout 並編譯。
編譯以後,用法也很簡單,一看就能懂了。
P.S.
由於這個專案沒有釋出二進位碼,所以你需要自己 checkout 並編譯。
編譯以後,用法也很簡單,一看就能懂了。
import Boo.PatternMatching // match 與 case 這兩個 macro 都在這裡面 def getEnglish( i as int ) as string: s = "" match i: case 0: s = "zero" case 1: s = "one" case 2: s = "two" case 3: s = "three" otherwise: s = "unknown" return s l = array( typeof(int), range( 5 ) ) for item in l: print getEnglish( item )未來 Boo extensions 會包進 Boo 嗎?很難說...Boo extensions 目前仍然很具實驗性...
P.S.
- 編譯Boo extensions前,請下載最新的 Boo,然後解壓縮以後,放到跟 boo-extensions 同一層。再切換到 boo-extensions/extensions 下執行 nant 即可。
- 如果 Boo extensions 無法編譯成功,試著修改 extensions/default.build 將編譯 .Test.dll 的幾個地方註解掉,再次編譯即可。這些 .Test.dll 其實是用不到的。