Files
wren/doc/site/expressions.markdown
2015-03-13 07:24:45 -07:00

452 lines
12 KiB
Markdown

^title Expressions
^category language
Wren's syntax is based on C so if you're familiar with that (or any of the
plethora of other languages based on it) you should be right at home. Since
Wren is heavily object-oriented, you'll notice that most of the different
expression forms are just different ways of invoking methods.
## Literals
Literals produce objects of built-in types. The primitive [value](values.html)
types—numbers, strings and friends—have literal forms as do the
built in collections: [lists](lists.html) and [maps](maps.html).
[Functions](functions.html) do not have standalone a literal form. Instead,
they are created by passing a [block
argument](functions.html#block-arguments) to a method.
## Identifiers
Names in expressions come in a few flavors. A name that starts with an
underscore denotes a [field](classes.html#fields), a piece of data stored in an
instance of a [class](classes.html). All other names refer to
[variables](variables.html).
## Method calls
Wren is object-oriented, so most code consists of method calls. Most of them
look like so:
:::dart
IO.print("hello")
items.add("another")
items.insert(1, "value")
You have a *receiver* expression followed by a `.`, then a name and an argument
list in parentheses. Arguments are separated by commas. Methods that do not
take any arguments can omit the `()`:
:::dart
text.length
These are special "getters" or "accessors" in other languages. In Wren, they're
just method calls. You can also define methods that take an empty argument list:
:::dart
list.clear()
An empty argument list is *not* the same as omitting the parentheses
completely. Wren lets you overload methods by their call signature. This mainly
means [*arity*](classes.html#signature)—number of parameters—but
also distinguishes between "empty parentheses" and "no parentheses at all".
You can have a class that defines both `foo` and `foo()` as separate methods.
Think of it like the parentheses and commas between arguments are part of the
method's *name*.
If the last (or only) argument to a method call is a
[function](functions.html), it may be passed as a [block
argument](functions.html#block-arguments):
:::dart
blondie.callMeAt(867, 5309) {
IO.print("This is the body!")
}
Semantically, all method calls work like so:
1. Evaluate the receiver and arguments.
2. Look up the method on the receiver's class.
3. Invoke it, passing in the arguments.
## This
The special `this` keyword works sort of like a variable, but has special
behavior. It always refers to the instance whose method is currently being
executed. This lets you invoke methods on "yourself".
It's an error to refer to `this` outside of a method. However, it's perfectly
fine to use it inside a function contained in a method. When you do, `this`
still refers to the instance whose method is being called.
This is unlike Lua and JavaScript which can "forget" `this` when you create a
callback inside a method. Wren does what you want here and retains the
reference to the original object. (In technical terms, a function's closure
includes `this`.)
## Super
Sometimes you want to invoke a method on yourself, but only methods defined in
one of your [superclasses](classes.html#inheritance). You typically do this in
an overridden method when you want to access the original method being
overridden.
To do that, you can use the special `super` keyword as the receiver in a method
call:
:::dart
class Base {
method {
IO.print("base method")
}
}
class Derived is Base {
method {
super.method // Prints "base method".
}
}
You can also use `super` without a method name inside a constructor to invoke a
base class constructor:
:::dart
class Base {
new(arg) {
IO.print("base constructor got ", arg)
}
}
class Derived is Base {
new {
super("value") // Prints "base constructor got value".
}
}
**TODO: constructors**
## Operators
Wren has most of the same operators you know and love with the same precedence
and associativity. Wren has three prefix operators:
:::dart
! ~ -
They are just method calls on their operand without any other arguments. An
expression like `!possible` means "call the `!` method on `possible`".
We have a few other operators to play with. The remaining ones are
infix—they have operators on either side. They are:
:::dart
== != < > <= >= .. ... | & + - * / %
Like prefix operators, they are all funny ways of writing method calls. The
left operand is the receiver, and the right operand gets passed to it. So
`a + b` is semantically interpreted as "invoke the `+` method on `a`, passing
it `b`".
Most of these are probably familiar already. The `..` and `...` operators are
"range" operators. The number type implements those to create a
[range](values.html#ranges) object, but they are just regular operators.
Since operators are just method calls, this means Wren supports "operator
overloading" (though "operator over-*riding*" is more accurate). This can be
really useful when the operator is natural for what a class represents, but can
lead to mind-crushingly unreadable code when used recklessly. There's a reason
punctuation represents profanity in comic strips.
## Assignment
The `=` operator is used to *assign* or store a value somewhere. The right-hand
side can be any expression. If the left-hand side is an
[identifier](#identifiers), then the value of the right operand is stored in
the referenced [variable](variables.html) or [field](classes.html#fields).
The left-hand side may also be a method call, like:
:::dart
point.x = 123
In this case, the entire expression is a single "setter" method call. The above
example invokes the `x=` setter on the `point` object, and passing in `123`.
Sort of like `point.x=(123)`.
Since these are just regular method calls, you can define whatever setters you
like in your classes. However, you cannot change the behavior of *simple*
assignment. If the left-hand side is a variable name or field, an assignment
expression will always just store the value there.
## Subscript operators
Most languages use square brackets (`[]`) for working with collection-like
objects. For example:
:::dart
list[0] // Gets the first item in a list.
map["key"] // Gets the value associated with "key".
You know the refrain by now. In Wren, these are just method calls that a class
may define. Subscript operators may take multiple arguments, which is useful
for things like multi-dimensional arrays:
:::dart
matrix[3, 5]
Subscripts may also be used on the left-hand side of an assignment:
:::dart
list[0] = "item"
map["key"] = "value"
Again, these are just method calls. The last example is equivalent to invoking
the `[]=` method on `map`, passing in `"key"` and `"value"`.
## Logical operators
The `&&` and `||` operators are not like the other infix operators. They work
more like [control flow](control-flow.html) structures than operators because
they conditionally execute some code&mdash;they short-circuit. Depending on the
value of the left-hand side, the right-hand operand expression may or may not
be evaluated. Because of this, they cannot be overloaded and their behavior is
fixed.
A `&&` ("logical and") expression evaluates the left-hand argument. If it's
[false](control-flow.html#truth), it returns that value. Otherwise it evaluates
and returns the right-hand argument.
:::dart
IO.print(false && 1) // false
IO.print(1 && 2) // 2
An `||` ("logical or") expression is reversed. If the left-hand argument is
[true](control-flow.html#truth), it's returned, otherwise the right-hand
argument is evaluated and returned:
:::dart
IO.print(false || 1) // 1
IO.print(1 || 2) // 1
## The conditional operator `?:`
Also known as the "ternary" operator since it takes three arguments, Wren has
the little "if statement in the form of an expression" you know and love from C
and its brethren.
:::dart
IO.print(1 != 2 ? "math is sane" : "math is not sane!")
It takes a condition expression, followed by `?`, followed by a then
expression, a `:`, then an else expression. Just like `if`, it evaluates the
condition. If true, it evaluates (and returns) the then expression. Otherwise
it does the else expression.
## The `is` operator
Wren has one last expression form. You can use the `is` keyword like an infix
operator. It performs a type test. The left operand is an object and the right
operand is a class. It evaluates to `true` if the object is an instance of the
class (or one of its subclasses).
:::dart
123 is Num // true
"s" is Num // false
null is String // false
[] is List // true
[] is Sequence // true
## Precedence
When you mix these all together, you need to worry about
*precedence*&mdash;which operators bind more tightly than others&mdash;and
*associativity*&mdash;how a series of the same operator is ordered. Wren mostly
follows C, except that it fixes the bitwise operator mistake. The full
precedence table, from highest to lowest, is:
<table class="precedence">
<tbody>
<tr>
<th>Precedence</th>
<th>Operator</th>
<th>Description</th>
<th>Associativity</th>
</tr>
<tr>
<td>1</td>
<td>()</td>
<td>Call</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>1</td>
<td>[]</td>
<td>Subscript</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>1</td>
<td>.</td>
<td>Selection</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>2</td>
<td>-</td>
<td>Unary minus</td>
<td>Right-to-left</td>
</tr>
<tr>
<td>2</td>
<td>!</td>
<td>Logical not</td>
<td>Right-to-left</td>
</tr>
<tr>
<td>2</td>
<td>~</td>
<td>Bitwise not</td>
<td>Right-to-left</td>
</tr>
<tr>
<td>3</td>
<td>*</td>
<td>Multiplication</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>3</td>
<td>/</td>
<td>Division</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>3</td>
<td>%</td>
<td>Modulo</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>4</td>
<td>+</td>
<td>Addition</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>4</td>
<td>-</td>
<td>Subtraction</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>5</td>
<td>..</td>
<td>Range (inclusive)</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>5</td>
<td>...</td>
<td>Range (half-inclusive)</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>6</td>
<td>&lt;&lt;</td>
<td>Bitwise left shift</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>6</td>
<td>&gt;&gt;</td>
<td>Bitwise right shift</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>7</td>
<td>&lt;</td>
<td>Less than</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>7</td>
<td>&lt;=</td>
<td>Less than or equal to</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>7</td>
<td>&gt;</td>
<td>Greater than</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>7</td>
<td>&gt;=</td>
<td>Greater than or equal to</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>8</td>
<td>==</td>
<td>Equal to</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>8</td>
<td>!=</td>
<td>Not equal to</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>9</td>
<td>&amp;</td>
<td>Bitwise AND</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>10</td>
<td>^</td>
<td>Bitwise XOR</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>11</td>
<td>|</td>
<td>Bitwise OR</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>12</td>
<td>is</td>
<td>Type test</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>13</td>
<td>&amp;&amp;</td>
<td>Logical AND</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>14</td>
<td>||</td>
<td>Logical OR</td>
<td>Left-to-right</td>
</tr>
<tr>
<td>15</td>
<td>?:</td>
<td>Ternary Conditional</td>
<td>Right-to-left</td>
</tr>
<tr>
<td>16</td>
<td>=</td>
<td>Assignment</td>
<td>Right-to-left</td>
</tr>
</tbody>
</table>