Io 語言是一門動態的基於原型的語言,語言模型主要參考了 Smalltalk (全部值都是對象), Self(基於原型), NewtonScript(差別化繼承), Act1(併發模型), Lisp(運行時可檢測和修改代碼樹)和 Lua(小巧可嵌入性)等語言的特性.git
過去三十年, 人們對編程語言的關注,已經轉移到了具有強大的表達力的高級語言(好比 Ruby Python Javascript),以及性能卓越的底層語言(好比 C C++ Java). 結果就是一系列的中間語言(既不如 C 快,又不如 Ruby Javascript 那樣有強大的表達力)逐漸成爲過去.程序員
Io 語言的目的,就是充分把高級動態語言的特性 -- 運行時動態擴展和簡潔的語法發揮出來, 從新把語言的重點放在語言的表達能力上.github
在 Io 語言中,全部變量都是對象,全部變量均可以在運行時改變,包括 槽(slot)
, 方法和其繼承關係. 代碼是由表達式組成,能夠在運行時任意查看和修改; 全部表達式都隱含了一個動態的信息發送,包括賦值結構和控制結構也是同樣.express
不斷變化的上下文自己就是對象,函數, 代碼塊和命名空間也都是能夠動態的賦值並進行傳遞的對象. 經過 actors 使得語言的併發特性變得更容易實現,而且使用 coroutines 機制讓其具有更大的擴展能力.編程
Io 語言被設計成:vim
能夠直接從這個網站下載已經編譯好的可執行文件:數組
http://iobin.suspended-chord.info/
也能夠到官方網站了解詳情:緩存
http://iolanguage.com
如下測試僅在 Ubuntu 12.04 server 版本上得到經過:網絡
sudo apt-get install git-common git clone https://github.com/stevedekorte/io.git sudo apt-get install gcc g++ lib2.0 cmake make cd io mkdir build cd build cmake .. make install
在 Windows 平臺上 Mingw/cygwin 環境中均由於 lib2.0 庫文件不全而沒法編譯。併發
從源碼中能夠獲取語言在各類編輯器上的語法高亮腳本:
源碼地址(近期開始活躍):
https://github.com/stevedekorte/io.git
各類編輯器的語法高亮目錄:
io-master\extras\SyntaxHighlighters
Vim 高亮文件:
https://github.com/andreimaxim/vim-io
參考教程:
七週七語言第二章 # stackoverflow 上的一些 Io language 的問題 http://stackoverflow.com/questions/tagged/iolanguage http://www.chrisumbel.com/article/io_language_prototype http://www.ituring.com.cn/tupubarticle/1664 http://www.bennadel.com/blog/2064-seven-languages-in-seven-weeks-io-day-1.htm
Io 會創建兩份可執行文件而且把它們替換到二進制文件夾中:
io_static io
io_static 可執行文件包括了原始類型支持的最小集合,都是靜態連接到它上面的. io 可執行文件則能夠加載 iovm 動態連接庫,能夠在須要的時候動態加載 io 插件.
例子:
io samples/misc/HelloWorld.io
這種方式下不須要 main 方法或者對象,腳本在編譯後就執行了.
運行:
./_build/binaries/io
也能夠直接輸入:
io
這樣就打開了解釋器的交互模式.
你能夠在交互模式下直接輸入代碼來執行,好比:
Io> "Hello world!" println ==> Hello world!
表達式在 Lobby 的上下文中執行:
Io> print [printout of lobby contents]
若是你在 home
文件夾中有一個 .iorc 文件,它會在解釋器模式加載前執行.
你能夠得到一個對象的槽 (slot) 的列表:
Io> someObject := Object clone Io> someObject slotNames
若是要排序顯示出來:
Io> someObject slotNames sort
要以漂亮的格式來展現,slotSummary
方法很便捷:
Io> slotSummary ==> Object_0x20c4e0: Lobby = Object_0x20c4e0 Protos = Object_0x20bff0 exit = method(...) forward = method(...)
更進一步查看:
Io> Protos ==> Object_0x20bff0: Addons = Object_0x20c6f0 Core = Object_0x20c4b0 Io> Protos Addons ==> Object_0x20c6f0: ReadLine = Object_0x366a10
看起來只有 ReadLine
在插件裏面,沒有別的了.
查看一個方法,會打印出沒有編譯的方法代碼:
Io> Lobby getSlot("forward") ==> # io/Z_Importer.io:65 method( Importer import(call) )
腳本文件能夠在交互模式下使用 doFile 方法直接運行:
doFile("scriptName.io")
doFile 所在對象的上下文環境是被稱爲 receiver,在這裏就是默認的頂級對象 Lobby
. 想在別的對象所在的上下文環境中執行腳本的話,能夠把 doFile 消息發送給它.
someObject doFile("scriptName.io")
doString 方法能夠用來執行生成一個 string :
Io> doString("1+1") ==> 2
一樣,也能夠指定一個上下文:
someObject doString("1 + 1")
打印出命令行參數的示例:
System args foreach(k, v, write("'", v, "'\n"))
System 的 launchPath
槽用來設定初始源文件執行的位置. 當交互模式提示符出現(不顯式指定源文件路徑),launchPath 就是當前的工做目錄:
System launchPath
Io 語言沒有關鍵字或者聲明語句. 一切都是由表達式組成的消息,每個都是在運行時能夠被訪問的對象. 正式的 BNF 表述以下:
exp ::= { message | terminator } message ::= symbol [arguments] arguments ::= "(" [exp [ { "," exp } ]] ")" symbol ::= identifier | number | string terminator ::= "\n" | ";"
考慮到性能的緣由,String 和 Number 字面消息會緩存結果在消息對象中.
消息的參數以表達式的方式傳入,並在 receiver 中執行.
根據參數取值選擇邏輯能夠用來實現控制流,好比:
for(i, 1, 10, i println) a := if(b == 0, c + 1, d)
上面的代碼中,for 和 if 都是普通的消息而已,並非什麼關鍵字.
相似的,動態賦值能夠用來實現枚舉的功能,而不須要一個封閉的代碼塊:
people select(person, person age < 30) names := people map(person, person name)
像 map, select 之類的方法每每被用於表達式直接過濾值集合:
people select(age < 30) names := people map(name)
有一些運算符 (包括賦值) 的語法糖,在編譯成消息樹之後被 Io 的宏執行:
Account := Object clone Account balance := 0 Account deposit := method(amount, balance = balance + amount ) account := Account clone account deposit(10.00) account balance println
像 Self 語言同樣, Io 的語法並不區分訪問的是一個方法仍是一個直接量.
操做符只是一個普通的消息,並且這個消息不包含任何字母 (or, and 和 return 除外):
1 + 2
它會被編譯成:
1 +(2)
若是你須要括號分組:
1 +(2 * 4)
符合C的優先級順序:
1 + 2 * 3 + 4
會被轉換爲:
1 +(2 *(3)) +(4)
用戶定義的操做符(非標準的操做符名字)是自左向右結合運算的.
Io 有三種賦值運算符:
operator action ::= Creates slot, creates setter, assigns value := Creates slot, assigns value = Assigns value to slot if it exists, otherwise raises exception
賦值運算符也是普通消息,它們的方法均可以被覆寫:
source compiles to a ::= 1 newSlot("a", 1) a := 1 setSlot("a", 1) a = 1 updateSlot("a", 1)
本地對象中,updateSlot 被覆寫,若是這個槽不存在,這個槽就會被更新到對象中. 這個操做不須要顯式的聲明就能夠作到.
有效的數字格式:
123 123.456 0.456 .456 123e-4 123e4 123.456e-7 123.456e2
十六進制數亦被支持(不限大小寫):
0x0 0x0F 0XeE
字符串用一組雙引號 + 轉義符來定義:
s := "this is a \"test\".\nThis is only a test."
或者,不使用轉義字符的話能夠寫成多行(用三個雙引號):
s := """this is a "test". This is only a test."""
註釋支持 //, /**/ 和 # 風格:
a := b // add a comment to a line /* comment out a group a := 1 b := 2 */
#
風格在 Unix 中頗有用:
#!/usr/local/bin/io
就這些!一切都是表達式. 這些就是控制流, 對象, 方法, 異常和其餘語法表達式,以及語義.
經過對概念的統一,Io 的導引描述了簡單和強大的原則.
concept unifies scopable blocks functions, methods, closures prototypes objects, classes, namespaces, locals messages operators, calls, assigns, var access
一切都是對象,包括存儲代碼塊的本地變量和命名空間,而全部對象又都是消息 (包括賦值操做符). 對象能夠由一串鍵值對組成,他們被稱爲 槽(slot)
,每個對象都自內部對象繼承而來,被繼承的對象被稱爲 原型(proto)
. 槽的鍵能夠是一個符號(惟一的無心義序列),值能夠是任意對象類型.
新建的對象經過 clone 已有對象來實現. clone 出來的東西實際上是一個空對象,可是在它的原型列表中,能夠找到它的父對象. 在這個過程後,新建對象的init槽會被調用,以便初始化對象本身. 就像NewtonScript同樣,槽在Io中是符合 建立-寫入
模式的:
me := Person clone
添加一個實例變量或者方法:
myDog name := "rover" myDog sit := method("I'm sitting\n" print)
若是存在 init 方法的話,clone 後 init 會被自動調用.
對象收到消息的時候,若是消息符合該對象的某一個槽,就執行,若是找不到,那麼就一層一層向上找它的原型. 查找循環會被檢測到,它是不被容許的. 若是匹配的槽包含一個可激活的對象,好比Block或者CFunction,它包含任何其餘值類型,就會被遞歸調用. Io命名空間中沒有全局變量,根對象被稱爲Lobby.
由於沒有類,只有原型,那麼子類和實例自己也就沒有任何區別了. 這裏有一個建立相同子類的例子:
Io> Dog := Object clone ==> Object_0x4a7c0
以上代碼設置了Lobby的槽 Dog
爲 Object 對象,這個新對象的原型列表只包含一個對 Object 的引用,本質上指明瞭這就是Object的子類. 實例變量和方法繼承自這個原型列表. 若是設置了這個槽,建立新槽並不會改變它的原型對象:
Io> Dog color := "red" Io> Dog ==> Object_0x4a7c0: color := "red"
你能夠添加任意數量的原型到對象的原型列表上. 當對象收到一個消息的時候,它會深度搜索原型鏈尋找匹配.
方法就是一種匿名函數,調用的時候,會建立一個對象存儲在本地變量集合裏面,而且設置這個對象的原型指針和它本身的槽到相應的消息上面. 這個Object的method()方法能夠用來建立對象:
method((2 + 2) print)
對象中使用這個 method 的例子:
Dog := Object clone Dog bark := method("woof!" print)
以上代碼建立了一個 Object 的子類 Dog
,而且添加了 bark
槽,這個槽包含了打印 woof!
的代碼塊. 調用示例:
Dog bark
默認返回值就是最後一行表達式語句的結果.
方法定義的時候能夠帶參數:
add := method(a, b, a + b)
總的來講,形式以下:
method(<arg name 0>, <arg name 1>, ..., <do message>)
除去詞法的變量範圍之外,代碼塊和方法同樣. 變量查找會在代碼塊建立的上下文中繼續,而不是在調用代碼塊的消息的上下文中繼續. 一個代碼塊可使用 Object 的 block() 方法來建立:
b := block(a, a + b)
有時候這兩個概念會引發混亂,因此有必要再細細解釋一下. 代碼塊和方法均可以建立在被調用時能夠持有本地變量的對象. 不一樣之處就在於本地對象的 proto
和 self
槽設置到哪裏去. 方法中,這些槽設置到消息對象的目標去,而在代碼塊中,它們是被設置到代碼塊建立時的本地對象中去的. 因此一次失敗的變量查找,將引發在代碼塊建立上下文的本地變量中繼續查找,而方法則是在消息接收者中繼續查找.
當本地對象被建立,self槽被設置
當本地對象建立,它的self槽和call槽會被設置到Call對象中,以用以獲取關於代碼塊調用的信息:
slot returns call sender locals object of caller call message message used to call this method/block call activated the activated method/block call slotContext context in which slot was found call target current object
本地變量的 call message
槽能夠用來獲取不定參數消息. 見 if() 的實現:
myif := method( (call sender doMessage(call message argAt(0))) ifTrue( call sender doMessage(call message argAt(1))) ifFalse( call sender doMessage(call message argAt(2))) ) myif(foo == bar, write("true\n"), write("false\n"))
doMessage() 方法把參數傳入 receiver 的上下文中,一個簡潔的寫法是使用 evalArgAt():
myif := method( call evalArgAt(0) ifTrue( call evalArgAt(1)) ifFalse( call evalArgAt(2)) ) myif(foo == bar, write("true\n"), write("false\n"))
若是對象不響應消息,並且 forward 方法存在的話,會調用 forward 方法. 這個例子是顯示怎樣打印查找失敗消息的:
MyObject forward := method( write("sender = ", call sender, "\n") write("message name = ", call message name, "\n") args := call message argsEvaluatedIn(call sender) args foreach(i, v, write("arg", i, " = ", v, "\n") ) )
以 self 做爲上下文,將當前消息發給 receiver 的原型鏈:
A := Object clone A m := method(write("in A\n")) B := A clone B m := method(write("in B\n"); resend) B m
打印:
in B in A
重發其餘消息給 receiver 的原型時,會使用到 super.
若是你須要直接發消息給原型:
Dog := Object clone Dog bark := method(writeln("woof!")) fido := Dog clone fido bark := method( writeln("ruf!") super(bark) )
重發和 super 都在 Io 中實現了.
使用如下方法你能夠自省 Io 的命名空間. 也有方法用來在運行時修改這些屬性值.
slotNames 方法返回一個對象槽名字的列表:
Io> Dog slotNames ==> list("bark")
protos方法返回對象繼承的一個列表:
Io> Dog protos ==> list("Object")
getSlot 方法用於獲取一個槽的實際值:
myMethod := Dog getSlot("bark")
前文中,咱們設置了本地對象的 myMethod 槽爲 bark方法. 你若是須要 myMethod 方法自己,可是又不調用它,你能夠經過 getSlot 來獲取:
otherObject newMethod := getSlot("myMethod")
方法的參數和表達式是能夠自省的. 一個好用的辦法的就是 code 方法,返回一個源代碼的字符串:
Io> method(a, a * 2) code ==> "method(a, a *(2))"
true, false 和 nil
true, false 和 nil 都是單例,nil通常用於標識一個未設置值或者丟失了的值.
比較方法:
==, !=, >=, <=, >, <
返回 true 或者 false,compare() 方法用於實現真正的比較,返回 -1, 0 或者 1,分別表示小於, 相等和大於.
if() 方法的形式:
if(<condition>, <do message>, <else do message>)
例子:
if(a == 10, "a is 10" print)
else 參數是可選的. 若是條件表達式執行爲 false 或者 nil,都被視做 false.
執行結果也能夠返回:
if(y < 10, x := y, x := 0)
和以下形式同樣:
x := if(y < 10, y, 0)
條件能夠這樣用:
if(y < 10) then(x := y) else(x := 2)
支持 elseif():
if(y < 10) then(x := y) elseif(y == 11) then(x := 0) else(x := 2)
支持 Smalltalk 風格的 ifTrue, ifFalse, ifNil 和 ifNonNil 方法:
(y < 10) ifTrue(x := y) ifFalse(x := 2)
注意: 條件表達式必須用圓括號括起來.
循環方法支持無限循環:
loop("foo" println)
Number 的 repeat 方法能夠用於把一個對象的方法重複執行你給定的次數:
3 repeat("foo" print) ==> foofoofoo
形式:
while(<condition>, <do message>) a := 1 while(a < 10, a print a = a + 1 )
形式:
for(<counter>, <start>, <end>, <optional step>, <do message>)
start 和 end 消息只在循環開始時執行一次:
for(a, 0, 10, a println )
使用 step:
for(x, 0, 10, 3, x println)
打印:
0 3 6 9
要反轉這個循環的話,使用負值做爲 step:
for(a, 10, 0, -1, a println)
注: first 值是循環的起始值,last 值是標誌環完成時的值,因此 1 到 10 的循環會執行 10 次而 0 到 10 的循環會執行 11 次.
loop, repeat, while 和 for 都是支持 break 和 continue 的:
for(i, 1, 10, if(i == 3, continue) if(i == 7, break) i print )
輸出:
12456
代碼塊中執行到任何語句時均可以返回:
Io> test := method(123 print; return "abc"; 456 print) Io> test 123 ==> abc
break, continue 和 return 都經過存取 stopStatus 這個監控循環和消息的內部值來工做.
Importer 原型實現了 Io 內置的引入機制. 你只須要把你的原型放到它們各自的文件中,文件名以 io 結尾,這個 Importer 在原型第一次被用到的時候會自動引入它們. 默認的搜索路徑是當前工做目錄,你也能夠調用 addSearchPath() 來添加路徑.
Io 使用 coroutines (協程,這裏指用戶層面的線程協做) 而不是操做系統搶佔式的線程實現方式. 這就減小了潛在的由於本地線程和數千個活躍線程切換引發的消耗(內存, 系統調用, 鎖和緩存問題等等).
Scheduler 對象用來從新開始已經掛起的 coroutines(協程,見上文). 當前的 scheduling 系統使用無優先級的FIFO策略.
actor 是一個擁有本身線程的對象(擁有本身的協程),用於處理異步消息隊列. 任何對象能夠以異步消息的方式發送出去,你只須要重寫 asyncSend() 或者 futureSend() 消息:
// synchronous result := self foo // async, immediately returns a Future futureResult := self futureSend(foo) // async, immediately returns nil self asyncSend(foo)
當對象接收到異步消息的時候,會把消息放到隊列裏面,若是隊列裏沒有的話,會啓動一個協程來處理隊列中的消息. 隊列消息是順序處理的(FIFO). Control 能夠經過調用 yield 用來掛起當前處理流程,讓出資源:
obj1 := Object clone obj1 test := method(for(n, 1, 3, n print; yield)) obj2 := obj1 clone obj1 asyncSend(test); obj2 asyncSend(test) while(Scheduler yieldingCoros size > 1, yield)
會打印出 112233,如下是一個更真實的例子:
HttpServer handleRequest := method(aSocket, HttpRequestHandler clone asyncSend(handleRequest(aSocket)) )
Io 的 future 是透明的,當結果準備好的時候,它們就是結果,若是一個消息被髮送給future,就會被掛起等待,直到結果準備好. 透明的 future 很強大,由於它們容許程序員儘量地減少阻塞,把程序員從繁重的異步管理的細節中解脫出來.
使用future的一個優勢是當等待的時候,它會檢測等待是否在等待中會產生死鎖,若是會的話就拋出異常.
Futures 和命令行接口
命令行會打印表達式的結果,若是結果是Future的話,直到結果返回再打印:
Io> q := method(wait(1)) Io> futureSend(q) [1-second delay] ==> nil
若是不想這樣,不返回Future就行:
Io> futureSend(q); nil [no delay] ==> nil Yield
對象的異步消息在執行的時候會自動轉讓控制權,yield方法只是在要明確讓出CPU資源的時候調用.
暫停和恢復對象,請參見併發方法部分.
在異常的原型中調用raise(),表示異常被拋出了.
Exception raise("generic foo exception")
要捕獲異常,使用Object原型的try()方法. try()會捕獲任何異常並返回,若是沒有異常就返回nil.
e := try(<doMessage>)
To catch a particular exception, the Exception catch() method can be used. Example:
e := try( // ... ) e catch(Exception, writeln(e coroutine backtraceString) )
第一個參數是要捕獲的異常類型,catch()返回異常.
要從新拋出異常的話,使用 pass 方法. 它能夠把異常拋給下一個外部的異常處理器,咱們每每在全部的 catch 都沒法捕獲到所需異常的時候拋出:
e := try( // ... ) e catch(Error, // ... ) catch(Exception, // ... ) pass
自定義異常類型能夠 clone Exception 來實現:
MyErrorType := Error clone
Primitives 是一組 Io 內建的對象,它們的方法一般使用 C 實現而且存放了一些隱含的數據在內. 舉例來講,Number 包含了一個 double 精度的浮點數,而且能夠像 C 函數同樣計算. 全部的 Io 內置對象都繼承自 Object 原型,而且是不可變對象. 也就是說這些方法都是不可變的.
這這篇文檔不是想要成爲參考手冊,只不過是想提供一個全部內置對象的概覽,給用戶一個入門,提供一個基礎認識,要了解詳情請參見參考手冊.
有時候想要在某方法存在的狀況下調用(避免拋出找不到方法的異常):
if(obj getSlot("foo"), obj foo)
能夠用問號操做符寫成這樣:
obj ? foo
列表是一個存放引用的數組,支持標準的數組操做和遍歷方法.
建立空列表:
a := List clone
用list()方法建立任意列表:
a := list(33, "a")
添加元素:
a append("b") ==> list(33, "a", "b")
獲取大小:
a size ==> 3
根據下標獲取元素(從0開始):
a at(1) ==> "a"
設置元素:
a atPut(2, "foo") ==> list(33, "a", "foo", "b") a atPut(6, "Fred") ==> Exception: index out of bounds
刪除元素:
a remove("foo") ==> list(33, "a", "b")
插入:
a atInsert(2, "foo") ==> list(33, "a", "foo", "56")
foreach, map 和 select 方法能夠以三種形式調用:
Io> a := list(65, 21, 122) Io> a foreach(i, v, write(i, ":", v, ", ")) ==> 0:65, 1:21, 2:122,
第二種形式是略去下標的:
Io> a foreach(v, v println) ==> 65 21 122
第三種形式:
Io> a foreach(println) ==> 65 21 122
map 和 select(有的語言裏面叫 filter)方法容許執行任意表達式:
Io> numbers := list(1, 2, 3, 4, 5, 6) Io> numbers select(isOdd) ==> list(1, 3, 5) Io> numbers select(x, x isOdd) ==> list(1, 3, 5) Io> numbers select(i, x, x isOdd) ==> list(1, 3, 5) Io> numbers map(x, x*2) ==> list(2, 4, 6, 8, 10, 12) Io> numbers map(i, x, x+i) ==> list(1, 3, 5, 7, 9, 11) Io> numbers map(*3) ==> list(3, 6, 9, 12, 15, 18)
map 和 select 方法返回新的列表,若是直接在原有列表上操做,可使用 selectInPlace() 和 mapInPlace().
不變的 Sequence 被稱爲 Symbol,而可變的 Sequence 和 Buffer 或者 String 是等價的. Literal strings(被雙引號引發來的這種string)就是 Symbols,它們是不能改變的. 可是調用 asMutable 能夠生成一個可變的字符串:
"abc" size ==> 3
檢查是否存在子串:
"apples" containsSeq("ppl") ==> true
獲取第N個char(byte):
"Kavi" at(1) ==> 97
切割:
"Kirikuro" slice(0, 2) ==> "Ki" "Kirikuro" slice(-2) # NOT: slice(-2, 0)! ==> "ro" Io> "Kirikuro" slice(0, -2) # "Kiriku"
去空格:
" abc " asMutable strip ==> "abc" " abc " asMutable lstrip ==> "abc " " abc " asMutable rstrip ==> " abc"
大小寫轉換:
"Kavi" asUppercase ==> "KAVI" "Kavi" asLowercase ==> "kavi"
切割:
"the quick brown fox" split ==> list("the", "quick", "brown", "fox")
根據其它字符切割:
"a few good men" split("e") ==> list("a f", "w good m", "n")
轉換成 Number:
"13" asNumber ==> 13 "a13" asNumber ==> nil
字符串插入:
name := "Fred" ==> Fred "My name is #{name}" interpolate ==> My name is Fred
字符串插入會代換 #{} 裏面的東西,代碼能夠包含循環等等,可是最後必須返回一個 String.
一個包含起始和結束,還有怎樣從開始執行到結束的指令. 當建立一個很大的序列數據列表的時候會頗有用,它能夠很容易被轉成list,或者使用for()被替換.
每一個對象均可以用在 Ranges 裏面,只要實現 nextInSequence 方法. 這個方法接受一個可選參數,表示跳過(skip)多少個對象,而後返回下一個對象,默認的這個skip值是1:
Number nextInSequence := method(skipVal, if(skipVal isNil, skipVal = 1) self + skipVal )
有了 Number 的這個方法,你就可使用 Number 的 Range 了:
1 to(5) foreach(v, v println)
上面代碼會打印 1 到 5 ,一個一行.
幾個方法:
openForAppending, openForReading, openForUpdating,刪除的話就是 remove 方法:
f := File with("foo.txt) f remove f openForUpdating f write("hello world!") f close
建立一個文件夾對象:
dir := Directory with("/Users/steve/")
獲取一個文件對象的列表:
files := dir files ==> list(File_0x820c40, File_0x820c40, ...)
獲取文件 + 文件夾列表:
items := Directory items ==> list(Directory_0x8446b0, File_0x820c40, ...) items at(4) name ==> DarkSide-0.0.1 # a directory name
創建一個文件夾對象:
root := Directory clone setPath("c:/") ==> Directory_0x8637b8 root fileNames ==> list("AUTOEXEC.BAT", "boot.ini", "CONFIG.SYS", …)
測試文件是否存在:
Directory clone setPath("q:/") exists ==> false
獲取當前工做目錄:
Directory currentWorkingDirectory ==> "/cygdrive/c/lang/IoFull-Cygwin-2006-04-20"
建立一個日期實例:
d := Date clone
設置到當前時間:
d now
以number方式獲取時間:
Date now asNumber ==> 1147198509.417114
獲取時間的每一部分:
d := Date now ==> 2006-05-09 21:53:03 EST d ==> 2006-05-09 21:53:03 EST d year ==> 2006 d month ==> 5 d day ==> 9 d hour ==> 21 d minute ==> 53 d second ==> 3.747125
看執行代碼的時間是多少:
Date cpuSecondsToRun(100000 repeat(1+1)) ==> 0.02
Io 支持異步鏈接,可是像讀寫 socket 的行爲是同步的,由於調用 coroutine 是沒法預期的,直到 socket 完成這步操做,或者超時出現.
建立一個 URL 對象:
url := URL with(http://example.com/)
獲取一個 URL:
data := url fetch
文件流:
url streamTo(File with("out.txt"))
一個簡單的 whois 客戶端:
whois := method(host, socket := Socket clone \ setHostName("rs.internic.net") setPort(43) socket connect streamWrite(host, "\n") while(socket streamReadNextChunk, nil) return socket readBuffer )
最簡單的服務端:
WebRequest := Object clone do( handleSocket := method(aSocket, aSocket streamReadNextChunk request := aSocket readBuffer
betweenSeq("GET ", " HTTP") f := File with(request) if(f exists, f streamTo(aSocket) , aSocket streamWrite("not found") ) aSocket close ) ) WebServer := Server clone do( setPort(8000) handleSocket := method(aSocket, WebRequest clone asyncSend(handleSocket(aSocket)) ) )
WebServer start
使用XML轉換器來找到網頁的連接:
SGML // reference this to load the SGML addon xml := URL with("http://www.yahoo.com/") fetch asXML links := xml elementsWithName("a") map(attributes at("href"))
Vector用在Sequence原語類型上面,定義爲:
Vector := Sequence clone setItemType("float32")
Sequence原語類型支持浮點32位操做的SIMD加強. 當前包含add, subtract, multiple和divide,可是將來會支持更多數學, 邏輯和字符串操做. 小例子:
iters := 1000 size := 1024 ops := iters * size v1 := Vector clone setSize(size) rangeFill v2 := Vector clone setSize(size) rangeFill dt := Date secondsToRun( iters repeat(v1 *= v2) ) writeln((ops/(dt*1000000000)) asString(1, 3), " GFLOPS")
在2Ghz的Mac筆記本上跑會輸出:
1.255 GFLOPS
類似的C代碼(SIMD強化)會輸出:
0.479 GFLOPS
從這個例子看,Io 是要比單純的 C 快大概三倍.
符號, 字符串和 vectors 都統一到一個 Sequence 原型中,Sequence 是一個包含全部可用的硬件數據類型的數組:
uint8, uint16, uint32, uint64 int8, int16, int32, int64 float32, float64
Sequence 有編碼屬性:
number, ascii, ucs2, ucs4, utf8
UCS-2和UCS-4分別是 UTF-16, UTF-32 的等寬字符版本. String 只不過是一個帶有文本編碼的 Sequence 而已; Symbol (符號)是不可變的 String ; 而Vector則是具有數字編碼的 Sequence 罷了.
UTF編碼是高位優先存儲的.
除了輸入輸出,全部的字符串都是等寬編碼. 這種設計帶來了簡化實現,代碼層面也能夠分享vector和string的操做,根據下標快速訪問,以及Sequence操做的SIMD支持. 全部Sequence方法會自動作必要的類型轉換.
Io 的源文件使用 UTF8 編碼,當源文件被讀入,符號和字符串被存儲時按照最小等寬編碼. 例子:
Io> "hello" encoding ==> ascii Io> "π" encoding ==> ucs2 Io> "∞" encoding ==> ucs2
來查看內部實現:
Io> "π" itemType ==> uint16 Io> "π" itemSize ==> 2
轉換
Sequence 對象有一組轉換方法:
asUTF8 asUCS2 asUCS4
Io 的實現代碼 C 代碼是按照面向對象風格來完成的,結構體成了對象,而函數變成了方法. 熟悉這些會幫助你理解那些嵌入式的 API.
成員都是小寫字母開頭,駱駝命名法:
typdef struct { char *firstName; char *lastName; char *address; } Person;
函數
函數命名以結構體開頭,而後用一下劃線鏈接,再加上真正的方法名. 每個結構體都有一個 new 函數和一個 free 函數:
List *List_new(void); void List_free(List *self);
全部除了 new 之外的方法都有一個做爲首參的結構 -- self.方法名是符合關鍵字的格式,也就是說,用下劃線串起一組詞來描述這個方法:
int List_count(List *self); // no argument void List_add_(List *self, void *item); // one argument void Dictionary_key_value_(Dictionary *self, char *key, char *value);
每個結構體都有它本身的 .h 和 .c 文件. 除去後綴,文件名和結構體名字一致. 文件包含該結構體全部的方法.
IoState 能夠被認爲是 Io 的 虛擬機
的實例. 儘管這裏 虛擬機
並不許確,由於它意味着某種特定的實現類型.
Io是多狀態的,這意味着它被設計來支持多種狀態實例,而這些實例能夠存在在同一個進程中. 這些實例互相獨立,不共享內存,因此它們能夠被不一樣操做系統線程訪問,儘管同一時間只能訪問一個.
這裏有一個建立和變動狀態的例子:
#include "IoState.h" int main(int argc, const char *argv[]) { IoState *self = IoState_new(); IoState_init(self); IoState_doCString_(self, "writeln(\"hello world!\""); IoState_free(self); return 0; } Values
咱們能夠獲取返回值而且查看和打印:
IoObject *v = IoState_doCString_(self, someString); char *name = IoObject_name(v); printf("return type is a ‘%s', name); IoObject_print(v);
有一些簡便的宏來作快速的類型檢查:
if (ISNUMBER(v)) { printf("result is the number %f", IoNumber_asFloat(v)); } else if(ISSEQ(v)) { printf("result is the string %s", IoSeq_asCString(v)); } else if(ISLIST(v)) { printf("result is a list with %i elements", IoList_rawSize(v)); }
注: 返回值老是 Io 對象,你能夠在 Io/libs/iovm/source 的頭文件中找到 C 級別的方法,好比這樣的函數: IoList_rawSize().
expression ::= { message | sctpad } message ::= [wcpad] symbol [scpad] [arguments] arguments ::= Open [argument [ { Comma argument } ]] Close argument ::= [wcpad] expression [wcpad]
symbol ::= Identifier | number | Operator | quote Identifier ::= { letter | digit | "_" } Operator ::= { ":" | "." | "'" | "~" | "!" | "@" | "$" | "%" | "^" | "&" | "*" | "-" | "+" | "/" | "=" | "{" | "}" | "[" | "]" | "|" | "\" | "<" | ">" | "?" }
quote ::= MonoQuote | TriQuote MonoQuote ::= """ [ "\"" | not(""")] """ TriQuote ::= """"" [ not(""""")] """""
Terminator ::= { [separator] ";" | "\n" | "\r" [separator] } separator ::= { " " | "\f" | "\t" | "\v" } whitespace ::= { " " | "\f" | "\r" | "\t" | "\v" | "\n" } sctpad ::= { separator | Comment | Terminator } scpad ::= { separator | Comment } wcpad ::= { whitespace | Comment }
Comment ::= slashStarComment | slashSlashComment | poundComment slashStarComment ::= "/*" [not("*/")] "*/" slashSlashComment ::= "//" [not("\n")] "\n" poundComment ::= "#" [not("\n")] "\n"
number ::= HexNumber | Decimal HexNumber ::= "0" anyCase("x") { [ digit | hexLetter ] } hexLetter ::= "a" | "b" | "c" | "d" | "e" | "f" Decimal ::= digits | "." digits | digits "." digits ["e" [-] digits]
Comma ::= "," Open ::= "(" | "[" | "{" Close ::= ")" | "]" | "}" letter ::= "a" ... "z" | "A" ... "Z" digit ::= "0" ... "9" digits ::= { digit }
上面的表達式中的大寫字母的單詞 lexer 會當成字元 tokens.
Io is the product of all the talented folks who taken the time and interest to make a contribution. The complete list of contributors is difficult to keep track of, but some of the recent major contributors include; Jonathan Wright, Jeremy Tregunna, Mike Austin, Chris Double, Rich Collins, Oliver Ansaldi, James Burgess, Baptist Heyman, Ken Kerahone, Christian Thater, Brian Mitchell, Zachary Bir and many more. The mailing list archives, repo inventory and release history are probably the best sources for a more complete record of individual contributions.
1. Goldberg, A et al. Smalltalk-80: The Language and Its Implementation Addison-Wesley, 1983 2. Ungar, D and Smith, RB. Self: The Power of Simplicity OOPSLA, 1987 3. Smith, W. Class-based NewtonScript Programming PIE Developers magazine, Jan 1994 4. Lieberman H. Concurrent Object-Oriented Programming in Act 1 MIT AI Lab, 1987 5. McCarthy, J et al. LISP I programmer's manual MIT Press, 1960 6. Ierusalimschy, R, et al. Lua: an extensible extension language John Wiley & Sons, 1996
Copyright 2006-2010 Steve Dekorte. All rights reserved.
Redistribution and use of this document with or without modification, are permitted provided that the copies reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
This documentation is provided "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the authors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this documentation, even if advised of the possibility of such damage.