Simplicity is the essence of happiness.Io はプロトタイプベースの動的言語である。 その着想の大半は Smalltalk (全てがオブジェクトである)、Self[2] (プロトタイプベース), NewtonScript[3] (差分継承), Act1 (アクタとFutureによる並行動作)、Lisp[5] (コードは 検査 / 修正 可能なランタイムツリー) そして Lua[6] (小さい、組み込み可能).
- Cedric Bledsoe
Io は 全ての値はオブジェクト (継承されたメソッド、スロットを含んでいる場合でも、 何でもランタイムに変更可能)、 全てのコードは式 (それはランタイムに操作、変更可能) であり、 全ての式は動的なメッセージを送信 (代入と制御構造を含む)することで成り立っています。 実行コンテキストは、オブジェクトの集まりとメソッド / ブロックによる活性可能なオブジェクト、そしてブロックによる代入可能なスコープに統一された関数だけです。 並行処理は、アクターとスケーラビリティの為にコルーチンを通して、より簡単に操作しやすく作られています。
シンプル
http://iolanguage.com
make vm sudo make install
su -c "sudo make aptget"または:
su -c "make emerge"または:
sudo make portあなたの使用しているパッケージインストーラによって使い分けでください。 (port is for OSX)
makeバイナリはサブフォルダ _build/binaries に置かれるでしょう。 インストールするには:
sudo make installもしくは、インストールを、単にあなたの開発用フォルダにリンクするようにするには:
sudo make linkInstallそしてすべてのユニットテストを実行するには:
make testいくつかのアドオンがビルドできなくても、そのアドオンがあなたが特別必用としていないなら、それは心配しなくても大丈夫です。 アドオンは任意のライブラリです。
make AddonNameソースコントロール上のリポジトリから取得してきた後は、必ずしてください:
make clean; makeVM のテストのみ実行:
make testvm
io_static ioio_static は最小量のセットの基本関数が静的にすべてリンクされている vm を含んでいる実行可能な形式です。 io は実行時に、iovm が動的にリンクされたライブラリをロードすることができます。それは、io のアドオンが参照されたときに動的に組み込まれます。
io samples/HelloWorld.ioIo が最初に実行するものに、どんな main() 関数やオブジェクトも必要ありません。 スクリプトはコンパイルされると実行されます。
./_build/binaries/ioもし Io がインストール済みなら、以下のように実行:
ioIo のインタプリタのプロンプトが引かれるでしょう。
直接入力する事で、コードを評価することができます。 例:
Io> "Hello world!" println ==> Hello world!式は Lobby のコンテキストにて評価される :
Io> print [lobby のコンテンツが出力される]あなたのホームフォルダに .iorc が存在する場合、対話モードのプロンプトが開始される前にそれが評価されます。
Io> someObject slotNamesそれらをソート順にするには:
Io> someObject slotNames sortオブジェクトの記述がうまくフォーマットされた形式においては slotSummary メソッドが便利です:
Io> slotSummary ==> Object_0x20c4e0: Lobby = Object_0x20c4e0 Protos = Object_0x20bff0 exit = method(...) forward = method(...)Exploring further:
Io> Protos ==> Object_0x20bff0: Addons = Object_0x20c6f0 Core = Object_0x20c4b0 Io> Protos Addons ==> Object_0x20c6f0: ReadLine = Object_0x366a10他の Addons が組み込まれていないので、RadLine だけが Addons に表示されています。
メソッドをインスペクトすると、その時のバージョンで逆コンパイルされたものが表示されるでしょう:
Io> Lobby getSlot("forward")
==> # io/Z_Importer.io:65
method(
Importer import(call)
)
doFile("scriptName.io")
doFile はレシーバのコンテキストにて評価します。(この場合レシーバは lobby でしょう)。 他のオブジェクトのコンテキストにてスクリプトを評価するには、 doFile メッセージをそれに送信します:
someObject doFile("scriptName.io")
文字列を評価するには doString メソッドを使用することができます:
Io> doString("1+1")
==> 2
また、特定のオブジェクトのコンテキストで文字列を評価するには:
someObject doString("1 + 1")
args foreach(k, v, write("'", v, "'\n"))
exp ::= { message | terminator }
message ::= symbol [arguments]
arguments ::= "(" [exp [ { "," exp } ]] ")"
symbol ::= identifier | number | string
terminator ::= "\n" | ";"
For performance reasons, String and Number literal messages have their results cached in their message objects.
for(i, 1, 10, i println) a := if(b == 0, c + 1, d)上記コードでの、"for" と "if" は通常のメッセージであり、特殊形式やキーワードでは無い。
Likewise, dynamic evaluation can be used with enumeration without the need to wrap the expression in a block. 例:
people select(person, person age < 30) names := people map(person, person name)Methods like map and select will typically apply the expression directly to the values if only the expression is provided:
people select(age < 30) names := people map(name)There is also some syntax sugar for operators (including assignment), which are handled by an Io macro executed on the expression after it is compiled into a message tree. Some sample source code:
Account := Object clone
Account balance := 0
Account deposit := method(amount,
balance = balance + amount
)
account := Account clone
account deposit(10.00)
account balance println
Like Self[2], Io's syntax does not distinguish between accessing a slot containing a method from one containing a variable.
1 + 2すなわち次のような通常の形式のメッセージにコンパイルされる:
1 +(2)括弧を使ってまとめることもできる:
1 +(2 * 4)標準の演算子は、C 言語と同じ優先順位で処理される。従って:
1 + 2 * 3 + 4は次のように解析される:
1 +(2 *(3)) +(4)ユーザ定義の演算子 (標準の演算子名は使えない) は、常に左から右に動作する。
| operator | action |
| ::= | スロットの生成, setter の生成, 値の代入 |
| := | スロットの生成, 値の代入 |
| = | スロットが存在すれば値の代入、もしなければ例外を発生する |
These operators are compiled to normal messages whose methods can be overridden. 例:
| ソースコード | コンパイル結果 |
| a ::= 1 | newSlot("a", 1) |
| a := 1 | setSlot("a", 1) |
| a = 1 | updateSlot("a", 1) |
On Locals objects, updateSlot is overridden so it will update the slot in the object in which the method was activated if the slot is not found the locals. This is done so update assignments in methods don't require self to be an explicit target.
123 123.456 0.456 .456 123e-4 123e4 123.456e-7 123.456e216進数もサポートされます(どんなケースでも):
0x0 0x0F 0XeE
s := "this is a \"test\".\nThis is only a test."Or for strings with non-escaped characters and/or spanning many lines, triple quotes can be used.
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 |
| blocks with assignable scope | functions, methods, closures, functions |
| prototypes | objects, classes, namespaces, locals |
| messages | operators, calls, assignment, variable accesses |
me := Person cloneインスタンスに変数やメソッドを追加するには、単純にセットします:
myDog name := "rover"
myDog sit := method("I'm sitting\n" print)
あるオブジェクトのクローンが作成されると、その init スロットがあれば、それが呼ばれます。
クラスがないため、サブクラスとインスタンスの違いはありません。 以下は、サブクラスに相当するものを作成する例です:
Io> Dog := Object clone ==> Object_0x4a7c0上記のコードでは、Lobby の "Dog" スロットに Object オブジェクトのクローンをセットしています。 そのクローンは Object への参照を含む protos リストのみを持っています。 これで、本質的に Dog は Object のサブクラスとなります。 インスタンスの変数やメソッドはその proto から継承されます。 スロットがセットされた場合、proto は変更されず、作成したオブジェクトに新しいスロットを作成します:
Io> Dog color := "red"
Io> Dog
==> Object_0x4a7c0:
color := "red"
method((2 + 2) print)オブジェクト内のメソッドを使用する例:
Dog := Object clone
Dog bark := method("woof!" print)
上記コードは、Dog と名の付けられたオブジェクトの "サブクラス" を新たに作成し、"woof!" を出力するブロックである bark スロットを含んでいます。 メソッドを呼び出す例:
Dog barkブロックのデフォルトの戻り値は、最後の式の結果になります。
add := method(a, b, a + b)一般的な形式:
method(<arg name 0>, <arg name 1>, ..., <do message>)
b := block(a, a + b)
| slot | returns |
| call sender | 呼び出し側のローカルオブジェクト |
| call message | メソッドまたは、ブロックへの呼び出し時に使われたメッセージ |
| call activated | 実行されたメソッドまたはブロック |
| call slotContext | スロットが見つかった文脈 |
| call target | カレントオブジェクト |
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"))
The doMessage() method evaluates the argument in the context of the receiver.
A shorter way to express this is to use the evalArgAt() method on the call object:
myif := method(
call evalArgAt(0) ifTrue(
call evalArgAt(1)) ifFalse(
call evalArgAt(2))
)
myif(foo == bar, write("true\n"), write("false\n"))
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") )
)
A := Object clone
A m := method(write("in A\n"))
B := A clone
B m := method(write("in B\n"); resend)
B m
will print:
in B in AFor sending other messages to the receiver's proto, super is used.
Dog := Object clone
Dog bark := method(writeln("woof!"))
fido := Dog clone
fido bark := method(
writeln("ruf!")
super(bark)
)
Both resend and super are implemented in Io.
Io> Dog slotNames
==> list("bark")
Io> Dog protos
==> list("Object")
myMethod := Dog getSlot("bark")
Above, we've set the locals object's "myMethod" slot to the bark method. It's important to remember that if you then want use the myMethod without activating it, you'll need to use the getSlot method:
otherObject newMethod := getSlot("myMethod")
Here, the target of the getSlot method is the locals object.
Io> method(a, a * 2) code ==> "method(a, a *(2))"
Io> 1 < 2 ==> trueThe compare() method is used to implement the compare methods and returns -1, 0 or 1.
if(<condition>, <do message>, <else do message>)例:
if(a == 10, "a is 10" print)The else argument is optional. The condition is considered false if the condition expression evaluates to false or nil, and is considered true otherwise.
The result of the evaluated message is returned, so:
if(y < 10, x := y, x := 0)is the same as:
x := if(y < 10, y, 0)Conditions can also be used in this form (though not as efficiently):
if(y < 10) then(x := y) else(x := 2)Else-if is supported:
if(y < 10) then(x := y) elseif(y == 11) then(x := 0) else(x := 2)As well as Smalltalk style ifTrue, ifFalse, ifNil and ifNonNil methods:
(y < 10) ifTrue(x := y) ifFalse(x := 2)Notice that the condition expression must have parenthesis surrounding it.
loop("foo" println)
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
)
ステップによる例:
for(x, 0, 10, 3, x println)次の出力になります:
0 3 6 9ループを逆順に実行するには、負の値をステップに加えてください:
for(a, 10, 0, -1, a println)Note: the first value will be the first value of the loop variable and the last will be the last value on the final pass through the loop. So a loop of 1 to 10 will loop 10 times and a loop of 0 to 10 will loop 11 times. Example of using a block in a loop:
test := method(v, v print) for(i, 1, 10, test(i))
3 repeat("foo" print)
==> foofoofoo
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
例:
result := self foo // synchronous futureResult := self @foo // async, immediately returns a Future self @@foo // async, immediately returns nilWhen an object receives an asynchronous message it puts the message in its queue and, if it doesn't already have one, starts a coroutine to process the messages in its queue. Queued messages are processed sequentially in a first-in-first-out order. Control can be yielded to other coroutines by calling "yield". 例:
obj1 := Object clone obj1 test := method(for(n, 1, 3, n print; yield)) obj2 := obj1 clone obj1 @@test; obj2 @@test while(Scheduler yieldingCoroutines size > 1, yield)これは "112233" と出力される. Here's a more real world example:
HttpServer handleRequest := method(aSocket,
HttpRequestHandler clone @@handleRequest(aSocket)
)
self @testGets parsed as(and can be written as):
self @(test)
Io> q := method(wait(1)) Io> @q [1-second delay] ==> nilTo avoid this, just make sure the Future isn't the result. 例:
Io> @q; nil [no delay] ==> nil
Exception raise("generic foo exception")
e := try(<doMessage>)To catch a particular exception, the Exception catch() method can be used. 例:
e := try(
// ...
)
e catch(Exception,
writeln(e coroutine backtraceString)
)
The first argument to catch indicates which types of exceptions will be caught. catch() returns the exception if it doesn't match and nil if it does.
e := try(
// ...
)
e catch(Error,
// ...
) catch(Exception,
// ...
) pass
MyErrorType := Error clone
このドキュメントはリファレンスマニュアルではなく、基底プリミティブについての概説であり、さくっと触り、何が出来るかを書いています。リファレンスドキュメントのどこを見れば詳しく書いてあるかもわかるでしょう。
if(obj getSlot("foo"), obj foo)
Putting a "?" before a message has the same effect:
obj ?foo
空のリストを生成:
a := List clonelist() メソッドを使用することで、任意のリストを生成することができます:
a := list(33, "a")アイテムの追加:
a append("b")
==> list(33, "a", "b")
リストのサイズを取得:
a size ==> 3インデックスを指定し、アイテムの取得(リストのインデックスはゼロから開始されます):
a at(1) ==> "a"注: リストのインデックスはゼロから開始されます。また、アクセスしたインデックスが存在しない場合は nil を返します。
インデックスを指定し、アイテムのセット:
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 atPut(2, "foo") ==> list(33, "a", "foo", "56")
Io> a := list(65, 21, 122)In the first form, the first argument is used as an index variable, the second as a value variable and the 3rd as the expression to evaluate for each value.
Io> a foreach(i, v, write(i, ":", v, ", ")) ==> 0:65, 1:21, 2:122,The second form removes the index argument:
Io> a foreach(v, v println) ==> 65 21 122The third form removes the value argument and simply sends the expression as a message to each value:
Io> a foreach(println) ==> 65 21 122
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)The map and select methods return new lists. To do the same operations in-place, you can use selectInPlace() and mapInPlace() methods.
"abc" size ==> 3部分一致をチェック:
"apples" containsSeq("ppl")
==> true
位置Nの文字 (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 " adMutable 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")
数値へ変換:
"13" asNumber ==> 13 "a13" asNumber ==> nil文字列の補間:
name := "Fred"
==> Fred
"My name is #{name}" interpolate
==> My name is Fred
Interpolate は #{} で囲まれた全てを、Io のローカルの文脈コードとして評価します。 コードには制御構文も記述できますが、 asString に対応するオブジェクトを返却する必要があります。
Number nextInSequence := method(skipVal,
if(skipVal isNil, skipVal = 1)
self + skipVal
)
With this method on Number (it's already there in the standard libraries), you can then use Numbers in Ranges, as demonstrated below:
1 to(5) foreach(v, v println)The above will print 1 through 5, each on its own line.
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, ...)Get a list of both the file and directory objects in a directory:
items := Directory items ==> list(Directory_0x8446b0, File_0x820c40, ...) items at(4) name ==> DarkSide-0.0.1 # a directory nameSetting a Directory object to a certain directory and using it:
root := Directory clone setPath("c:/")
==> Directory_0x8637b8
root fileNames
==> list("AUTOEXEC.BAT", "boot.ini", "CONFIG.SYS", ...)
Testing for existence:
Directory clone setPath("q:/") exists
==> false
Getthing the current working directory:
Directory currentWorkingDirectory ==> "/cygdrive/c/lang/IoFull-Cygwin-2006-04-20"
d := Date cloneSetting it to the current date/time:
d nowGetting the date/time as a number, in seconds:
Date now asNumber ==> 1147198509.417114 Date now asNumber ==> 1147198512.33313Getting individual parts of a Date object:
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.747125Find how long it takes to execute some code:
Date cpuSecondsToRun(100000 repeat(1+1)) ==> 0.02
URL オブジェクトの生成:
url := URL with("http://example.com/")
Fetching an URL:
data := url fetchStreaming a URL to a file:
url streamTo(File with("out.txt"))
A simple whois client:
whois := method(host,
socket := Socket clone setHostName("rs.internic.net") setPort(43)
socket connect streamWrite(host, "\n")
while(socket streamReadNextChunk, nil)
return socket readBuffer
)
A minimal web server:
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 @handleSocket(aSocket)
)
)
WebServer start
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 clone setItemType("float32")
The Sequence primitive supports SIMD acceleration on a number of float32 operations. Currently these include add, subtract, multiple and divide but in the future can be extended to support most math, logic and string manipulation related operations.
Here's a small example:
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")
Which when run on 2Ghz Mac Laptop, outputs:
1.255 GFLOPSA similar bit of C code (without SIMD acceleration) outputs:
0.479 GFLOPSSo for this example, Io is about three times faster than plain C.
uint8, uint16, uint32, uint64 int8, int16, int32, int64 float32, float64
number, ascii, ucs2, ucs4, utf8UCS-2 and UCS-4 are the fixed character width versions of UTF-16 and UTF-32, respectively. A String is just a Sequence with a text encoding, a Symbol is an immutable String and a Vector is a Sequence with a number encoding.
UTF encodings are assumed to be big endian.
Except for input and output, all strings should be kept in a fixed character width encoding. This design allows for a simpler implementation, code sharing between vector and string ops, fast index-based access, and SIMD acceleration of Sequence operations. All Sequence methods will do automatic type conversions as needed.
Io> "hello" encoding ==> ascii Io> "π" encoding ==> ucs2 Io> "∞" encoding ==> ucs2We can also inspect the internal representation:
Io> "π" itemType ==> uint16 Io> "π" itemSize ==> 2
asUTF8 asUCS2 asUCS4
typdef struct
{
char *firstName;
char *lastName;
char *address;
} Person;
例:
List *List_new(void); void List_free(List *self);All methods (except new) have the structure (the "object") as the first argument the variable is named "self". Method names are in keyword format. That is, for each argument, the method name has a description followed by an underscore. The casing of the descriptions follow that of structure member names.
例:
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);
#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;
}
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));
}
Note that return values are always proper Io objects (as all values are objects in Io). You can find the C level methods (functions like IoList_rawSize()) for these objects in the header files in the folder Io/libs/iovm/source.
expression ::= { message | sctpad }
message ::= [wcpad] symbol [scpad] [arguments]
arguments ::= Open [argument [ { Comma argument } ]] Close
argument ::= [wcpad] expression [wcpad]
symbols
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 }
| 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 |
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.