Io's minimal grammar: expressions, messages, and operators.
Io has no keywords or statements. Everything is an expression composed entirely of messages, each of which is a runtime accessible object. The informal BNF description:
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.
Message arguments are passed as expressions and evaluated by the receiver. Selective evaluation of arguments can be used to implement control flow. Examples:
for(i, 1, 10, i println)
a := if(b == 0, c + 1, d)In the above code, "for" and "if" are just normal messages, not special forms or keywords.
Likewise, dynamic evaluation can be used with enumeration without the need to wrap the expression in a block. Examples:
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 printlnLike Self[2], Io's syntax does not distinguish between accessing a slot containing a method from one containing a variable.
An operator is just a message whose name contains no alphanumeric characters (other than ";", "_", '"' or ".") or is one of the following words: or, and, return. Example:
1 + 2This just gets compiled into the normal message:
1 +(2)Which is the form you can use if you need to do grouping:
1 +(2 * 4)Standard operators follow C's precedence order, so:
1 + 2 * 3 + 4Is parsed as:
1 +(2 *(3)) +(4)User defined operators (that don't have a standard operator name) are performed left to right.
Io has three assignment operators:
These operators are compiled to normal messages whose methods can be overridden.
For example:
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.
The following are examples of valid number formats:
123
123.456
0.456
.456
123e-4
123e4
123.456e-7
123.456e2Hex numbers are also supported (in any casing):
0x0
0x0F
0XeEStrings can be defined surrounded by a single set of double quotes with escaped quotes (and other escape characters) within.
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."""Comments of the //, /**/ and # style are supported. Examples:
a := b // add a comment to a line/* comment out a group
a := 1
b := 2
*/The "#" style is useful for unix scripts:
#!/usr/local/bin/ioThat's it! You now know everything there is to know about Io's syntax. Control flow, objects, methods, exceptions are expressed with the syntax and semantics described above.