forked from Mirror/wren
Reorganize the language guide.
- Rename "Expressions" -> "Method Calls". - Organize "Types" and "Language" into a single linear narrative. - Mobile-specific navigation to handle the longer guide. - Rename "Fibers" -> "Concurrency". - Get rid of duplicate stuff about signatures in "Classes". - Add next/prev links to each page in the guide. - Move "Contributing" and "Community" up to the top level. - Move the precendence table to a separate "Grammar" page. - Lots of other little stuff.
This commit is contained in:
@ -51,7 +51,7 @@ involved][contribute]!
|
||||
[nan]: https://github.com/munificent/wren/blob/46c1ba92492e9257aba6418403161072d640cb29/src/wren_value.h#L378-L433
|
||||
[perf]: http://munificent.github.io/wren/performance.html
|
||||
[classes]: http://munificent.github.io/wren/classes.html
|
||||
[fibers]: http://munificent.github.io/wren/fibers.html
|
||||
[fibers]: http://munificent.github.io/wren/concurrency.html
|
||||
[embedding]: http://munificent.github.io/wren/embedding-api.html
|
||||
[started]: http://munificent.github.io/wren/getting-started.html
|
||||
[browser]: http://ppvk.github.io/wren-nest/
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
^title Classes
|
||||
^category types
|
||||
^category guide
|
||||
|
||||
Every value in Wren is an object, and every object is an instance of a class.
|
||||
Even `true` and `false` are full-featured objects—instances of the
|
||||
[`Bool`](core/bool.html) class.
|
||||
|
||||
Classes contain both *behavior* and *state*. Behavior is defined in *methods*
|
||||
which are stored in the class. State is defined in *fields*, whose values are
|
||||
Classes define an objects *behavior* and *state*. Behavior is defined by
|
||||
[*methods*][method calls] which live in the class. Every object of the same
|
||||
class supports the same methods. State is defined in *fields*, whose values are
|
||||
stored in each instance.
|
||||
|
||||
[method calls]: method-calls.html
|
||||
|
||||
## Defining a class
|
||||
|
||||
Classes are created using the `class` keyword, unsurprisingly:
|
||||
@ -29,8 +32,8 @@ To let our unicorn do stuff, we need to give it methods.
|
||||
}
|
||||
}
|
||||
|
||||
This defines a `prance()` method that takes no arguments. To support
|
||||
parameters, put their names inside the parentheses:
|
||||
This defines a `prance()` method that takes no arguments. To add parameters, put
|
||||
their names inside the parentheses:
|
||||
|
||||
:::wren
|
||||
class Unicorn {
|
||||
@ -39,12 +42,10 @@ parameters, put their names inside the parentheses:
|
||||
}
|
||||
}
|
||||
|
||||
### Signature
|
||||
Since the number of parameters is part of a method's [signature][] a class can
|
||||
define multiple methods with the same name:
|
||||
|
||||
Unlike most other dynamically-typed languages, in Wren you can have multiple
|
||||
methods in a class with the same name, as long as they have a different
|
||||
*signature*. In technical terms, you can *overload by arity*. So this class is
|
||||
fine:
|
||||
[signature]: method-calls.html#signature
|
||||
|
||||
:::wren
|
||||
class Unicorn {
|
||||
@ -61,132 +62,105 @@ fine:
|
||||
}
|
||||
}
|
||||
|
||||
And you can call each of the methods like so:
|
||||
|
||||
:::wren
|
||||
var unicorn = Unicorn.new()
|
||||
unicorn.prance()
|
||||
unicorn.prance("Antwerp")
|
||||
unicorn.prance("Brussels", "high noon")
|
||||
|
||||
The number of arguments provided at the callsite determines which method is
|
||||
chosen.
|
||||
|
||||
It's often natural to have the same conceptual operation work with different
|
||||
sets of arguments. In other languages, you'd define a single method for the
|
||||
operation and have to check for "undefined" or missing arguments. Wren just
|
||||
treats them as different methods that you can implement separately.
|
||||
operation and have to check for missing optional arguments. In Wren, they are
|
||||
different methods that you implement separately.
|
||||
|
||||
In addition to named methods with parameter lists, Wren has a bunch of other
|
||||
different syntaxes for methods. Your classes can define all of them.
|
||||
|
||||
### Getters
|
||||
|
||||
Many methods on a class exist to expose or compute some property of the object.
|
||||
For example:
|
||||
|
||||
:::wren
|
||||
System.print("string".count) //> 6
|
||||
|
||||
These *getters* are just another kind of method—one without a parameter
|
||||
list. You can define them like so:
|
||||
A getter leaves off the parameter list and the parentheses:
|
||||
|
||||
:::wren
|
||||
class Unicorn {
|
||||
isFancy { true } // Unicorns are always fancy.
|
||||
}
|
||||
|
||||
Whether or not a method name has parentheses is also part of its signature.
|
||||
This lets you distinguish between a method that takes an *empty* argument list
|
||||
(`()`) and no argument list at all:
|
||||
### Setters
|
||||
|
||||
:::wren
|
||||
class Confusing {
|
||||
method { "no argument list" }
|
||||
method() { "empty argument list" }
|
||||
}
|
||||
|
||||
var confusing = Confusing.new()
|
||||
confusing.method // "no argument list".
|
||||
confusing.method() // "empty argument list".
|
||||
|
||||
Like the example says, having two methods that differ just by an empty set of
|
||||
parentheses is pretty confusing. That's not what this is for. Instead, it
|
||||
ensures that the way you *declare* the method is the way you *call* it.
|
||||
|
||||
Unlike other languages with "optional parentheses", Wren wants to make sure you
|
||||
call a getter like a getter and a `()` method like a `()` method. These don't
|
||||
work:
|
||||
|
||||
:::wren
|
||||
"string".count()
|
||||
list.clear
|
||||
|
||||
Methods that don't need arguments and don't modify the underlying object are
|
||||
usually getters:
|
||||
|
||||
:::wren
|
||||
"string".count
|
||||
(1..10).min
|
||||
1.23.sin
|
||||
[1, 2, 3].isEmpty
|
||||
|
||||
When a method doesn't need any parameters, but does modify the object, it's
|
||||
helpful to draw attention to that by requiring an empty set of parentheses:
|
||||
|
||||
:::wren
|
||||
list.clear()
|
||||
|
||||
Also, when a method supports multiple arities, it's typical to include the `()`
|
||||
in the zero-argument case to be consistent with the other versions:
|
||||
|
||||
Fn.new { "a function" }.call()
|
||||
Fiber.yield()
|
||||
|
||||
### Operators
|
||||
|
||||
Operators are just special syntax for a method call on the left hand operand
|
||||
(or only operand in the case of unary operators like `!` and `~`). In other
|
||||
words, you can think of `a + b` as meaning `a.+(b)`.
|
||||
|
||||
You can define operators in your class like so:
|
||||
A setter has `=` after the name, followed by a single parenthesized parameter:
|
||||
|
||||
:::wren
|
||||
class Unicorn {
|
||||
// Infix:
|
||||
+(other) {
|
||||
System.print("Adding to a unicorn?")
|
||||
}
|
||||
|
||||
// Prefix:
|
||||
! {
|
||||
System.print("Negating a unicorn?!")
|
||||
rider=(value) {
|
||||
System.print("I am being ridden by " + value)
|
||||
}
|
||||
}
|
||||
|
||||
This can be used to define any of these operators:
|
||||
### Operators
|
||||
|
||||
Prefix operators, like getters, have no parameter list:
|
||||
|
||||
:::wren
|
||||
// Infix:
|
||||
+ - * / % < > <= >= == != & |
|
||||
class Unicorn {
|
||||
- {
|
||||
System.print("Negating a unicorn is weird")
|
||||
}
|
||||
}
|
||||
|
||||
// Prefix:
|
||||
! ~ -
|
||||
Infix operators, like setters, have a single parenthesized parameter for the
|
||||
right-hand operand:
|
||||
|
||||
Note that `-` can be both a prefix and infix operator. If there's a parameter
|
||||
list, it's the infix one, otherwise, it's prefix. Since Wren supports
|
||||
overloading by arity, it's no problem for a class to define both.
|
||||
:::wren
|
||||
class Unicorn {
|
||||
-(other) {
|
||||
System.print("Subtracting " + other + " from a unicorn is weird")
|
||||
}
|
||||
}
|
||||
|
||||
### Subscript operators
|
||||
A subscript operator puts the parameters inside square brackets and can have
|
||||
more than one:
|
||||
|
||||
**TODO**
|
||||
:::wren
|
||||
class Unicorn {
|
||||
[index] {
|
||||
System.print("Unicorns are not lists!")
|
||||
}
|
||||
|
||||
### Setters
|
||||
[x, y] {
|
||||
System.print("Unicorns are not matrices either!")
|
||||
}
|
||||
}
|
||||
|
||||
**TODO**
|
||||
Unlike with named methods, you can't define a subscript operator with an empty
|
||||
parameter list.
|
||||
|
||||
As the name implies, a subscript setter looks like a combination of a subscript
|
||||
operator and a setter:
|
||||
|
||||
:::wren
|
||||
class Unicorn {
|
||||
[index]=(value) {
|
||||
System.print("You can't stuff " + value + " into me at " + index)
|
||||
}
|
||||
}
|
||||
|
||||
## This
|
||||
|
||||
**TODO: Integrate this into the page better.**
|
||||
|
||||
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`.)
|
||||
|
||||
## Constructors
|
||||
|
||||
Unicorns can prance around now, but we don't actually *have* any unicorns to do
|
||||
it. To create instances of a class, we need a *constructor*. You can define one
|
||||
like so:
|
||||
Unicorns can prance around now (as well as a bunch of weird operators that don't
|
||||
make sense outside of these examples), but we don't actually *have* any unicorns
|
||||
to do it. To create instances of a class, we need a *constructor*. You can
|
||||
define one like so:
|
||||
|
||||
:::wren
|
||||
class Unicorn {
|
||||
@ -196,10 +170,10 @@ like so:
|
||||
}
|
||||
|
||||
The `construct` keyword says we're defining a constructor, and `new` is its
|
||||
name. In Wren, all constructors have names, just like [methods][#methods]. The
|
||||
word "new" isn't special to Wren, it's just a common constructor name.
|
||||
name. In Wren, all constructors have names. The word "new" isn't special to
|
||||
Wren, it's just a common constructor name.
|
||||
|
||||
To make a unicorn now, we just call the constructor method on the class itself:
|
||||
To make a unicorn now, we call the constructor method on the class itself:
|
||||
|
||||
:::wren
|
||||
var fred = Unicorn.new("Fred", "palomino")
|
||||
@ -359,7 +333,10 @@ class using `is` when you declare the class:
|
||||
|
||||
This declares a new class `Pegasus` that inherits from `Unicorn`.
|
||||
|
||||
Note that you should not create classes that inherit from the built-in types (Bool, Num, String, Range, List). The built-in types expect their internal bit representation to be very specific and get horribly confused when you invoke one of the inherited built-in methods on the derived type.
|
||||
Note that you should not create classes that inherit from the built-in types
|
||||
(Bool, Num, String, Range, List). The built-in types expect their internal bit
|
||||
representation to be very specific and get horribly confused when you invoke one
|
||||
of the inherited built-in methods on the derived type.
|
||||
|
||||
The metaclass hierarchy does *not* parallel the regular class hierarchy. So, if
|
||||
`Pegasus` inherits from `Unicorn`, `Pegasus`'s metaclass will not inherit from
|
||||
@ -409,3 +386,48 @@ This means you can do `super` calls inside a constructor:
|
||||
}
|
||||
|
||||
Pegasus.new("Fred") //> My name is Fred
|
||||
|
||||
## Super
|
||||
|
||||
**TODO: Integrate better into page. Should explain this before mentioning
|
||||
super above.**
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
class Base {
|
||||
method() {
|
||||
System.print("base method")
|
||||
}
|
||||
}
|
||||
|
||||
class Derived is Base {
|
||||
method() {
|
||||
super.method() //> base method
|
||||
}
|
||||
}
|
||||
|
||||
You can also use `super` without a method name inside a constructor to invoke a
|
||||
base class constructor:
|
||||
|
||||
:::wren
|
||||
class Base {
|
||||
construct new(arg) {
|
||||
System.print("base got " + arg)
|
||||
}
|
||||
}
|
||||
|
||||
class Derived is Base {
|
||||
construct new() {
|
||||
super("value") //> base got value
|
||||
}
|
||||
}
|
||||
|
||||
<a class="right" href="concurrency.html">Concurrency →</a>
|
||||
<a href="functions.html">← Functions</a>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
^title Community
|
||||
^category reference
|
||||
|
||||
Like the [bird](https://en.wikipedia.org/wiki/Wren), Wren's community is small,
|
||||
but it exists!
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
^title Fibers
|
||||
^category types
|
||||
^title Concurrency
|
||||
^category guide
|
||||
|
||||
Fibers are a key part of Wren. They form its execution model, its concurrency
|
||||
story, and take the place of exceptions in [error
|
||||
handling](error-handling.html).
|
||||
Lightweight concurrency is a key feature of Wren and it is expressed using
|
||||
*fibers*. They control how all code is executed, and take the place of
|
||||
exceptions in [error handling](error-handling.html).
|
||||
|
||||
They are a bit like threads except they are *cooperatively* scheduled. That
|
||||
Fibers are a bit like threads except they are *cooperatively* scheduled. That
|
||||
means Wren doesn't pause one fiber and switch to another until you tell it to.
|
||||
You don't have to worry about context switches at random times and all of the
|
||||
headaches those cause.
|
||||
|
||||
Fibers are managed entirely by Wren, so they don't use OS thread resources, or
|
||||
require heavyweight context switches. They just need a bit of memory for their
|
||||
stacks. A fiber will get garbage collected like any other object when not
|
||||
referenced any more, so you can create them freely.
|
||||
Wren takes care of all of the fibers in the VM, so they don't use OS thread
|
||||
resources, or require heavyweight context switches. Each just needs a bit of
|
||||
memory for its stack. A fiber will get garbage collected like any other object
|
||||
when not referenced any more, so you can create them freely.
|
||||
|
||||
They are lightweight enough that you can, for example, have a separate fiber
|
||||
for each entity in a game. Wren can handle thousands of them without any
|
||||
trouble. For example, when you run Wren in interactive mode, it creates a new
|
||||
They are lightweight enough that you can, for example, have a separate fiber for
|
||||
each entity in a game. Wren can handle thousands of them without breaking a
|
||||
sweat. For example, when you run Wren in interactive mode, it creates a new
|
||||
fiber for every line of code you type in.
|
||||
|
||||
## Creating fibers
|
||||
@ -168,3 +168,6 @@ execution immediately to the transferred fiber. The previous one is suspended,
|
||||
leaving it in whatever state it was in. You can resume the previous fiber by
|
||||
transferring back to it, or even calling it. If you don't, execution stops when
|
||||
the last transferred fiber returns.
|
||||
|
||||
<a class="right" href="error-handling.html">Error Handling →</a>
|
||||
<a href="classes.html">← Classes</a>
|
||||
@ -1,8 +1,7 @@
|
||||
^title Contributing
|
||||
^category reference
|
||||
|
||||
It should be obvious by now that Wren is under active development and there's
|
||||
lots left to do. We'd be delighted to have you participate.
|
||||
Wren is under active development and there's lots to do. We'd be delighted to
|
||||
have you help!
|
||||
|
||||
## Getting acquainted
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
^title Control Flow
|
||||
^category language
|
||||
^category guide
|
||||
|
||||
Control flow is used to determine which chunks of code are executed and how
|
||||
many times. *Branching* statements decide whether or not to execute some code
|
||||
and *looping* ones execute something more than once.
|
||||
Control flow is used to determine which chunks of code are executed and how many
|
||||
times. *Branching* statements and expressions decide whether or not to execute
|
||||
some code and *looping* ones execute something more than once.
|
||||
|
||||
## Truth
|
||||
|
||||
All control flow is based on *deciding* whether or not to do something. This
|
||||
decision is conditional on the value of some expression. We take the entire
|
||||
universe of possible values and divide them into two buckets: some we consider
|
||||
"true" and the rest are "false". If the expression results in a value in the
|
||||
true bucket, we do one thing. Otherwise, we do something else.
|
||||
decision depends on some expression's value. We take the entire universe of
|
||||
possible objects and divide them into two buckets: some we consider "true" and
|
||||
the rest are "false". If the expression results in a value in the true bucket,
|
||||
we do one thing. Otherwise, we do something else.
|
||||
|
||||
Obviously, the boolean `true` is in the "true" bucket and `false` is in
|
||||
"false", but what about values of other types? The choice is ultimately
|
||||
@ -58,6 +58,45 @@ And, of course, it can take a block too:
|
||||
System.print("not ready!")
|
||||
}
|
||||
|
||||
## Logical operators
|
||||
|
||||
Unlike most other [operators][] in Wren which are just a special syntax for
|
||||
[method calls][], the `&&` and `||` operators are special. This is because they
|
||||
only conditionally evaluate right operand—they short-circuit.
|
||||
|
||||
[operators]: method-calls.html#operators
|
||||
[method calls]: method-calls.html
|
||||
|
||||
A `&&` ("logical and") expression evaluates the left-hand argument. If it's
|
||||
false, it returns that value. Otherwise it evaluates and returns the right-hand
|
||||
argument.
|
||||
|
||||
:::wren
|
||||
System.print(false && 1) //> false
|
||||
System.print(1 && 2) //> 2
|
||||
|
||||
A `||` ("logical or") expression is reversed. If the left-hand argument is
|
||||
*true*, it's returned, otherwise the right-hand argument is evaluated and
|
||||
returned:
|
||||
|
||||
:::wren
|
||||
System.print(false || 1) //> 1
|
||||
System.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.
|
||||
|
||||
:::wren
|
||||
System.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.
|
||||
|
||||
## While statements
|
||||
|
||||
It's hard to write a useful program without executing some chunk of code
|
||||
@ -119,16 +158,14 @@ A `for` loop has three components:
|
||||
|
||||
Sometimes, right in the middle of a loop body, you decide you want to bail out
|
||||
and stop. To do that, you can use a `break` statement. It's just the `break`
|
||||
keyword all by itself. That will immediately exit out of the nearest enclosing
|
||||
keyword all by itself. That immediately exits out of the nearest enclosing
|
||||
`while` or `for` loop.
|
||||
|
||||
:::wren
|
||||
for (i in [1, 2, 3, 4]) {
|
||||
System.print(i)
|
||||
if (i == 3) break
|
||||
}
|
||||
|
||||
So this program will print the numbers from 1 to 3, but will not print 4.
|
||||
System.print(i) //> 1
|
||||
if (i == 3) break //> 2
|
||||
} //> 3
|
||||
|
||||
## Numeric ranges
|
||||
|
||||
@ -149,12 +186,13 @@ leave off the last value, use three dots instead of two:
|
||||
System.print(i)
|
||||
}
|
||||
|
||||
This looks like some special "range" syntax in the `for` loop, but it's
|
||||
actually just a pair of operators. The `..` and `...` syntax are infix "range"
|
||||
operators. Like [other operators](expressions.html#operators), they are just
|
||||
special syntax for a regular method call. The number type implements them and
|
||||
returns a [range object](values.html#ranges) that knows how to iterate over a
|
||||
series of numbers.
|
||||
This looks like some special "range" syntax in the `for` loop, but it's actually
|
||||
just a pair of operators. The `..` and `...` syntax are infix "range" operators.
|
||||
Like [other operators][operators], they are special syntax for a regular method
|
||||
call. The number type implements them and returns a [range object][] that knows
|
||||
how to iterate over a series of numbers.
|
||||
|
||||
[range object]: values.html#ranges
|
||||
|
||||
## The iterator protocol
|
||||
|
||||
@ -202,3 +240,6 @@ that to look up and return the appropriate element.
|
||||
The built-in [List](lists.html) and [Range](values.html#ranges) types implement
|
||||
`iterate()` and `iteratorValue()` to walk over their respective sequences. You
|
||||
can implement the same methods in your classes to make your own types iterable.
|
||||
|
||||
<a class="right" href="variables.html">Variables →</a>
|
||||
<a href="method-calls.html">← Method Calls</a>
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
^title Fiber Class
|
||||
^category core
|
||||
|
||||
A lightweight coroutine. [Here](../fibers.html) is a gentle introduction.
|
||||
A lightweight coroutine. [Here][fibers] is a gentle introduction.
|
||||
|
||||
[fibers]: ../concurrency.html
|
||||
|
||||
### Fiber.**new**(function)
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ Otherwise, Wren spins up a new [fiber][] and executes the code in that. Your
|
||||
code can in turn spawn whatever other fibers it wants. It keeps running fibers
|
||||
until they all complete.
|
||||
|
||||
[fiber]: fibers.html
|
||||
[fiber]: concurrency.html
|
||||
|
||||
If a [runtime error][] occurs (and another fiber doesn't catch it), it will
|
||||
abort fibers all the way back to the main one and then return
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
^title Error Handling
|
||||
^category language
|
||||
^category guide
|
||||
|
||||
Errors come in a few fun flavors.
|
||||
|
||||
@ -90,11 +90,12 @@ Most of the time, runtime errors indicate a bug in your code and the best
|
||||
solution is to fix the bug. However, sometimes it's useful to be able to handle
|
||||
them at, uh, runtime.
|
||||
|
||||
To keep the language simpler, Wren does not have exception handling. Instead,
|
||||
it takes advantage of [fibers](fibers.html) for handling errors. When a runtime
|
||||
error occurs, the current fiber is aborted. Normally, Wren will also abort any
|
||||
fibers that invoked that one, all the way to the main fiber, and then exit the
|
||||
VM.
|
||||
To keep the language simpler, Wren does not have exception handling. Instead, it
|
||||
takes advantage of [fibers][] for handling errors. When a runtime error occurs,
|
||||
the current fiber is aborted. Normally, Wren will also abort any fibers that
|
||||
invoked that one, all the way to the main fiber, and then exit the VM.
|
||||
|
||||
[fibers]: concurrency.html
|
||||
|
||||
However, you can run a fiber using the `try` method. If a runtime error occurs
|
||||
in the called fiber, the error is captured and the `try` method returns the
|
||||
@ -163,3 +164,6 @@ indication.
|
||||
For example, a method for parsing a number could return a number on success and
|
||||
`null` to indicate parsing failed. Since Wren is dynamically typed, it's easy
|
||||
and natural for a method to return different types of values.
|
||||
|
||||
<a class="right" href="modules.html">Modules →</a>
|
||||
<a href="concurrency.html">← Concurrency</a>
|
||||
|
||||
@ -1,376 +0,0 @@
|
||||
^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:
|
||||
|
||||
:::wren
|
||||
System.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 `()`:
|
||||
|
||||
:::wren
|
||||
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:
|
||||
|
||||
:::wren
|
||||
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):
|
||||
|
||||
:::wren
|
||||
blondie.callMeAt(867, 5309) {
|
||||
System.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:
|
||||
|
||||
:::wren
|
||||
class Base {
|
||||
method() {
|
||||
System.print("base method")
|
||||
}
|
||||
}
|
||||
|
||||
class Derived is Base {
|
||||
method() {
|
||||
super.method() //> base method
|
||||
}
|
||||
}
|
||||
|
||||
You can also use `super` without a method name inside a constructor to invoke a
|
||||
base class constructor:
|
||||
|
||||
:::wren
|
||||
class Base {
|
||||
construct new(arg) {
|
||||
System.print("base got " + arg)
|
||||
}
|
||||
}
|
||||
|
||||
class Derived is Base {
|
||||
construct new() {
|
||||
super("value") //> base got value
|
||||
}
|
||||
}
|
||||
|
||||
## Operators
|
||||
|
||||
Wren has most of the same operators you know and love with the same precedence
|
||||
and associativity. Wren has three prefix operators:
|
||||
|
||||
:::wren
|
||||
! ~ -
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
== != < > <= >= .. ... | & + - * / %
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
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:
|
||||
|
||||
:::wren
|
||||
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:
|
||||
|
||||
:::wren
|
||||
matrix[3, 5]
|
||||
|
||||
Subscripts may also be used on the left-hand side of an assignment:
|
||||
|
||||
:::wren
|
||||
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—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.
|
||||
|
||||
:::wren
|
||||
System.print(false && 1) //> false
|
||||
System.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:
|
||||
|
||||
:::wren
|
||||
System.print(false || 1) //> 1
|
||||
System.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.
|
||||
|
||||
:::wren
|
||||
System.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).
|
||||
|
||||
:::wren
|
||||
System.print(123 is Num) //> true
|
||||
System.print("s" is Num) //> false
|
||||
System.print(null is String) //> false
|
||||
System.print([] is List) //> true
|
||||
System.print([] is Sequence) //> true
|
||||
|
||||
## Precedence
|
||||
|
||||
When you mix these all together, you need to worry about
|
||||
*precedence*—which operators bind more tightly than others—and
|
||||
*associativity*—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>Prec</th>
|
||||
<th>Operator</th>
|
||||
<th>Description</th>
|
||||
<th>Assoc</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td><code>()</code> <code>[]</code> <code>.</code></td>
|
||||
<td>Grouping, Subscript, Method call</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td><code>-</code> <code>!</code> <code>~</code></td>
|
||||
<td>Negate, Not, Complement</td>
|
||||
<td>Right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td><code>*</code> <code>/</code> <code>%</code></td>
|
||||
<td>Multiply, Divide, Modulo</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td><code>+</code> <code>-</code></td>
|
||||
<td>Add, Subtract</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td><code>..</code> <code>...</code></td>
|
||||
<td>Inclusive range, Exclusive range</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td><code><<</code> <code>>></code></td>
|
||||
<td>Left shift, Right shift</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td><code><</code> <code><=</code> <code>></code> <code>>=</code></td>
|
||||
<td>Comparison</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td><code>==</code></td>
|
||||
<td>Equals</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td><code>!=</code></td>
|
||||
<td>Not equal</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>9</td>
|
||||
<td><code>&</code></td>
|
||||
<td>Bitwise and</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>10</td>
|
||||
<td><code>^</code></td>
|
||||
<td>Bitwise xor</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11</td>
|
||||
<td><code>|</code></td>
|
||||
<td>Bitwise or</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>12</td>
|
||||
<td><code>is</code></td>
|
||||
<td>Type test</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>13</td>
|
||||
<td><code>&&</code></td>
|
||||
<td>Logical and</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>14</td>
|
||||
<td><code>||</code></td>
|
||||
<td>Logical or</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>15</td>
|
||||
<td><code>?:</code></td>
|
||||
<td>Conditional</td>
|
||||
<td>Right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>16</td>
|
||||
<td><code>=</code></td>
|
||||
<td>Assign</td>
|
||||
<td>Right</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -1,5 +1,5 @@
|
||||
^title Functions
|
||||
^category types
|
||||
^category guide
|
||||
|
||||
No self-respecting language today can get by without functions—first
|
||||
class little bundles of code. Since Wren is object-oriented, most of your code
|
||||
@ -161,3 +161,6 @@ to`i`:
|
||||
System.print(counter.call()) //> 1
|
||||
System.print(counter.call()) //> 2
|
||||
System.print(counter.call()) //> 3
|
||||
|
||||
<a class="right" href="classes.html">Classes →</a>
|
||||
<a href="variables.html">← Variables</a>
|
||||
|
||||
@ -93,9 +93,9 @@ Now run:
|
||||
:::sh
|
||||
$ ./wren my_script.wren
|
||||
|
||||
Neat, right? You're a Wren programmer now! The next step is to [read more
|
||||
docs](syntax.html) and learn your way around the language. If you run into
|
||||
bugs, or have ideas or questions, any of the following work:
|
||||
Neat, right? You're a Wren programmer now! The next step is to [learn the
|
||||
language](syntax.html). If you run into bugs, or have ideas or questions, any of
|
||||
the following work:
|
||||
|
||||
* **Ask on the [Wren mailing list][list].**
|
||||
* Tell me on twitter at [@munificentbob][twitter].
|
||||
|
||||
130
doc/site/grammar.markdown
Normal file
130
doc/site/grammar.markdown
Normal file
@ -0,0 +1,130 @@
|
||||
^title Grammar
|
||||
^category reference
|
||||
|
||||
**TODO: Fill in the rest of the grammar.**
|
||||
|
||||
## Precedence
|
||||
|
||||
When you mix the different [method call][] syntaxes and other [control flow][]
|
||||
operators together, you need to worry about *precedence*—which ones bind
|
||||
more tightly than others—and *associativity*—how a series of the
|
||||
same kind of call is ordered. Wren mostly follows C, except that it fixes [the
|
||||
bitwise operator mistake][mistake]. The full precedence table, from tightest to
|
||||
loosest, is:
|
||||
|
||||
[method call]: method-calls.html
|
||||
[control flow]: control-flow.html
|
||||
[mistake]: http://www.lysator.liu.se/c/dmr-on-or.html
|
||||
|
||||
<table class="precedence">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Prec</th>
|
||||
<th>Operator</th>
|
||||
<th>Description</th>
|
||||
<th>Assoc</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td><code>()</code> <code>[]</code> <code>.</code></td>
|
||||
<td>Grouping, Subscript, Method call</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td><code>-</code> <code>!</code> <code>~</code></td>
|
||||
<td>Negate, Not, Complement</td>
|
||||
<td>Right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td><code>*</code> <code>/</code> <code>%</code></td>
|
||||
<td>Multiply, Divide, Modulo</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td><code>+</code> <code>-</code></td>
|
||||
<td>Add, Subtract</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td><code>..</code> <code>...</code></td>
|
||||
<td>Inclusive range, Exclusive range</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td><code><<</code> <code>>></code></td>
|
||||
<td>Left shift, Right shift</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td><code><</code> <code><=</code> <code>></code> <code>>=</code></td>
|
||||
<td>Comparison</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td><code>==</code></td>
|
||||
<td>Equals</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td><code>!=</code></td>
|
||||
<td>Not equal</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>9</td>
|
||||
<td><code>&</code></td>
|
||||
<td>Bitwise and</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>10</td>
|
||||
<td><code>^</code></td>
|
||||
<td>Bitwise xor</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11</td>
|
||||
<td><code>|</code></td>
|
||||
<td>Bitwise or</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>12</td>
|
||||
<td><code>is</code></td>
|
||||
<td>Type test</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>13</td>
|
||||
<td><code>&&</code></td>
|
||||
<td>Logical and</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>14</td>
|
||||
<td><code>||</code></td>
|
||||
<td>Logical or</td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>15</td>
|
||||
<td><code>?:</code></td>
|
||||
<td>Conditional</td>
|
||||
<td>Right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>16</td>
|
||||
<td><code>=</code></td>
|
||||
<td>Assign</td>
|
||||
<td>Right</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -50,7 +50,7 @@ involved][contribute]!
|
||||
[nan]: https://github.com/munificent/wren/blob/46c1ba92492e9257aba6418403161072d640cb29/src/wren_value.h#L378-L433
|
||||
[perf]: performance.html
|
||||
[classes]: classes.html
|
||||
[fibers]: fibers.html
|
||||
[fibers]: concurrency.html
|
||||
[embedding]: embedding-api.html
|
||||
[started]: getting-started.html
|
||||
[browser]: http://ppvk.github.io/wren-nest/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
^title Lists
|
||||
^category types
|
||||
^category guide
|
||||
|
||||
A list is a compound object that holds a collection of elements identified by
|
||||
integer index. You can create a list by placing a sequence of comma-separated
|
||||
@ -14,9 +14,11 @@ have to be the same type.
|
||||
## Accessing elements
|
||||
|
||||
You can access an element from a list by calling the [subscript
|
||||
operator](expressions.html#subscript-operators) on it with the index of the
|
||||
operator][] on it with the index of the
|
||||
element you want. Like most languages, indexes start at zero:
|
||||
|
||||
[subscript operator]: method-calls.html#subscripts
|
||||
|
||||
:::wren
|
||||
var hirsute = ["sideburns", "porkchops", "'stache", "goatee"]
|
||||
hirsute[0] //> sideburns
|
||||
@ -109,3 +111,6 @@ If you want to remove everything from the list, you can clear it:
|
||||
:::wren
|
||||
hirsute.clear()
|
||||
System.print(hirsute) //> []
|
||||
|
||||
<a class="right" href="maps.html">Maps →</a>
|
||||
<a href="values.html">← Values</a>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
^title Maps
|
||||
^category types
|
||||
^category guide
|
||||
|
||||
A map is an *associative* collection. It holds a set of entries, each of which
|
||||
maps a *key* to a *value*. The same data structure has a variety of names in
|
||||
@ -21,15 +21,19 @@ names. Syntactically, in a map literal, keys can be any literal, a variable
|
||||
name, or a parenthesized expression. Values can be any expression. Here, we're
|
||||
using string literals for both keys and values.
|
||||
|
||||
*Semantically*, values can be any object, and multiple keys may map to the
|
||||
same value. Keys have a few limitations. They must be one of the immutable
|
||||
built-in [value types](values.html) in Wren. That means a number, string,
|
||||
range, bool, or `null`. You can also use a [class object](classes.html) as a
|
||||
key.
|
||||
*Semantically*, values can be any object, and multiple keys may map to the same
|
||||
value. Keys have a few limitations. They must be one of the immutable built-in
|
||||
[value types][] in Wren. That means a number, string, range, bool, or `null`.
|
||||
You can also use a [class object][] as a key.
|
||||
|
||||
In addition, even though they aren't strictly immutable, [fibers](fibers.html)
|
||||
can be used as map keys. This is handy for storing data that's roughly
|
||||
"thread-local" by using the current fiber as a map key.
|
||||
[value types]: values.html
|
||||
[class object]: classes.html
|
||||
|
||||
In addition, even though they aren't strictly immutable, [fibers][] can be used
|
||||
as map keys. This is handy for storing data that's roughly "thread-local" by
|
||||
using the current fiber as a map key.
|
||||
|
||||
[fibers]: concurrency.html
|
||||
|
||||
The reason for this limitation—and the reason maps are called "*hash*
|
||||
tables" in other languages—is that each key is used to generate a numeric
|
||||
@ -41,6 +45,8 @@ built-in types, only those can be used as keys.
|
||||
|
||||
You add new key-value pairs to the map by using the [subscript operator][]:
|
||||
|
||||
[subscript operator]: method-calls.html#subscripts
|
||||
|
||||
:::wren
|
||||
var capitals = {}
|
||||
capitals["Georgia"] = "Atlanta"
|
||||
@ -50,8 +56,6 @@ You add new key-value pairs to the map by using the [subscript operator][]:
|
||||
If the key isn't already present, this adds it and associates it with the given
|
||||
value. If the key is already there, this just replaces its value.
|
||||
|
||||
[subscript operator]: expressions.html#subscript-operators
|
||||
|
||||
## Looking up values
|
||||
|
||||
To find the value associated with some key, again you use your friend the
|
||||
@ -98,12 +102,12 @@ If the key wasn't in the map to begin with, `remove()` just returns `null`.
|
||||
If you want to remove *everything* from the map, just like with [lists][], you
|
||||
can just call `clear()`:
|
||||
|
||||
[lists]: lists.html
|
||||
|
||||
:::wren
|
||||
capitals.clear()
|
||||
System.print(capitals.count) //> 0
|
||||
|
||||
[lists]: lists.html
|
||||
|
||||
## Iterating over the contents
|
||||
|
||||
The subscript operator works well for finding values when you know the key
|
||||
@ -134,3 +138,6 @@ This program will print the three states and their birds. However, the *order*
|
||||
that they are printed isn't defined. Wren makes no promises about what order
|
||||
keys and values will be iterated in when you use these methods. All it promises
|
||||
is that every entry will appear exactly once.
|
||||
|
||||
<a class="right" href="method-calls.html">Method Calls →</a>
|
||||
<a href="lists.html">← Lists</a>
|
||||
|
||||
190
doc/site/method-calls.markdown
Normal file
190
doc/site/method-calls.markdown
Normal file
@ -0,0 +1,190 @@
|
||||
^title Method Calls
|
||||
^category guide
|
||||
|
||||
Wren is deeply object oriented, so most code consists of invoking methods on
|
||||
objects, usually something like this:
|
||||
|
||||
:::wren
|
||||
System.print("hello")
|
||||
|
||||
You have a *receiver* expression (here `System`) followed by a `.`, then a name
|
||||
(`print`) and an argument list in parentheses (`("hello")`). Multiple arguments
|
||||
are separated by commas:
|
||||
|
||||
:::wren
|
||||
list.insert("item", 3)
|
||||
|
||||
The argument list can also be empty:
|
||||
|
||||
:::wren
|
||||
list.clear()
|
||||
|
||||
Semantically, all method calls work like this:
|
||||
|
||||
1. Evaluate the receiver and arguments from left to right.
|
||||
2. Look up the method on the receiver's [class][].
|
||||
3. Invoke it, passing in the argument values.
|
||||
|
||||
[class]: classes.html
|
||||
|
||||
## Signature
|
||||
|
||||
Unlike most other dynamically-typed languages, in Wren a class can have multiple
|
||||
methods with the same *name*, as long as they have different *signatures*. The
|
||||
signature includes the method's name along with the number of arguments it
|
||||
takes. In technical terms, this means you can *overload by arity*.
|
||||
|
||||
<!-- TODO: Link to Random. -->
|
||||
|
||||
For example, the Random class has two methods for getting a random integer. One
|
||||
takes a minimum and maximum value and returns a value in that range. The other
|
||||
only takes a maximum value and uses 0 as the minimum:
|
||||
|
||||
:::wren
|
||||
var random = Random.new()
|
||||
random.int(3, 10)
|
||||
random.int(4)
|
||||
|
||||
In a language like Python or JavaScript, these would both call a single `int()`
|
||||
method, which has some kind of "optional" parameter. The body of the method
|
||||
figures out how many arguments were passed and uses control flow to handle the
|
||||
two different behaviors. That means first parameter represents "max unless
|
||||
another parameter was passed, in which case it's min". Kind of gross.
|
||||
|
||||
In Wren, these are calls to two entirely separate methods, `int(_,_)` and
|
||||
`int(_)`. This makes it easier to define "overloads" like this since you don't
|
||||
need optional parameters or any kind of control flow to handle the different
|
||||
cases.
|
||||
|
||||
It's also faster to execute. Since we know how many arguments are passed at
|
||||
compile time, we can compile this to directly call the right method and avoid
|
||||
any "if I got two arguments do this..." logic.
|
||||
|
||||
## Getters
|
||||
|
||||
Some methods exist to expose some stored or computed property of an object.
|
||||
These are *getters* and have no parentheses:
|
||||
|
||||
:::wren
|
||||
"string".count //> 6
|
||||
(1..10).min //> 1
|
||||
1.23.sin //> 0.9424888019317
|
||||
[1, 2, 3].isEmpty //> false
|
||||
|
||||
Sometimes you have a method doesn't need any parameters, but modifies the object
|
||||
or has some other side effect. For those, it's better to use empty parentheses:
|
||||
|
||||
:::wren
|
||||
list.clear()
|
||||
|
||||
Also, when a method supports multiple arities, it's typical to include the `()`
|
||||
in the zero-argument case to be consistent with the other versions:
|
||||
|
||||
Fiber.yield()
|
||||
Fiber.yield("value")
|
||||
|
||||
A getter is *not* the same as a method with an empty argument list. The `()` is
|
||||
part of the signature, so `count` and `count()` have different signatures.
|
||||
Unlike Ruby's optional parentheses, Wren wants to make sure you call a getter
|
||||
like a getter and a `()` method like a `()` method. These don't work:
|
||||
|
||||
:::wren
|
||||
"string".count()
|
||||
list.clear
|
||||
|
||||
## Setters
|
||||
|
||||
A getter lets an object expose a public "property" that you can *read*.
|
||||
Likewise, a *setter* let you write to a property:
|
||||
|
||||
:::wren
|
||||
person.height = 74 // Grew up!
|
||||
|
||||
Despite the `=`, this is just another syntax for a method call. From the
|
||||
language's perspective, the above line is just a call to the `height=(_)`
|
||||
method, passing in `74`.
|
||||
|
||||
Since the `=(_)` is in the setter's signature, an object can have both a getter
|
||||
and setter with the same name without any collision. This way, you can have
|
||||
read/write properties.
|
||||
|
||||
## Operators
|
||||
|
||||
Wren has most of the same operators you know and love with the same precedence
|
||||
and associativity. We have three prefix operators:
|
||||
|
||||
:::wren
|
||||
! ~ -
|
||||
|
||||
They are just method calls on their operand without any other arguments. An
|
||||
expression like `!possible` means "call the `!` method on `possible`".
|
||||
|
||||
We also have a slew of infix operators—they have operands on both sides.
|
||||
They are:
|
||||
|
||||
:::wren
|
||||
== != < > <= >= .. ... | & + - * / %
|
||||
|
||||
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`".
|
||||
|
||||
Note that `-` is both a prefix and an infix operator. Since they have different
|
||||
signatures (`-` and `-(_)`), there's no ambiguity between them.
|
||||
|
||||
Most of these are probably familiar already. The `..` and `...` operators are
|
||||
"range" operators. The number type implements those to create [range][]
|
||||
objects, but they are method calls like other operators.
|
||||
|
||||
[range]: values.html#ranges
|
||||
|
||||
## Subscripts
|
||||
|
||||
Another familiar syntax from math class is *subscripting* using square brackets
|
||||
(`[]`). It's handy for working with collection-like objects. For example:
|
||||
|
||||
:::wren
|
||||
list[0] // Get the first item in a list.
|
||||
map["key"] // Get the value associated with "key".
|
||||
|
||||
You know the refrain by now. In Wren, these are method calls. In the above
|
||||
examples, the signature is `[_]`. Subscript operators may also take multiple
|
||||
arguments, which is useful for things like multi-dimensional arrays:
|
||||
|
||||
:::wren
|
||||
matrix[3, 5]
|
||||
|
||||
These examples are subscript "getters", and there are also
|
||||
corresponding *subscript setters*:
|
||||
|
||||
:::wren
|
||||
list[0] = "item"
|
||||
map["key"] = "value"
|
||||
|
||||
These are equivalent to method calls whose signature is `[_]=(_)` and whose
|
||||
arguments are both the subscript (or subscripts) and the value on the right-hand
|
||||
side.
|
||||
|
||||
<!--
|
||||
|
||||
**TODO: Integrate this into the infix expressions when "is" becomes a normal
|
||||
overridable method.**
|
||||
|
||||
## 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).
|
||||
|
||||
:::wren
|
||||
System.print(123 is Num) //> true
|
||||
System.print("s" is Num) //> false
|
||||
System.print(null is String) //> false
|
||||
System.print([] is List) //> true
|
||||
System.print([] is Sequence) //> true
|
||||
|
||||
-->
|
||||
|
||||
<a class="right" href="control-flow.html">Control Flow →</a>
|
||||
<a href="maps.html">← Maps</a>
|
||||
@ -1,5 +1,5 @@
|
||||
^title Modules
|
||||
^category language
|
||||
^category guide
|
||||
|
||||
Once you start writing programs that are more than little toys, you quickly run
|
||||
into two problems:
|
||||
@ -283,3 +283,5 @@ Now when we run it, we get:
|
||||
This sounds super hairy, but that's because cyclic dependencies are hairy in
|
||||
general. The key point here is that Wren *can* handle them in the rare cases
|
||||
where you need them.
|
||||
|
||||
<a href="error-handling.html">← Error Handling</a>
|
||||
|
||||
@ -83,8 +83,10 @@ is that bytecode is a nice trade-off between performance and simplicity. Also:
|
||||
* Many devices like iPhones and game consoles don't allow executing code
|
||||
generated at runtime, which rules out just-in-time compilation.
|
||||
|
||||
* I think [fibers](fibers.html) are a really powerful tool, and implementing
|
||||
them is straightforward in a bytecode VM that doesn't use the native stack.
|
||||
* I think [fibers][] are a really powerful tool, and implementing them is
|
||||
straightforward in a bytecode VM that doesn't use the native stack.
|
||||
|
||||
[fibers]: concurrency.html
|
||||
|
||||
## Why is the VM stack-based instead of register-based?
|
||||
|
||||
|
||||
@ -105,7 +105,7 @@ header {
|
||||
nav {
|
||||
float: right;
|
||||
width: 160px;
|
||||
padding-top: 110px;
|
||||
padding-top: 109px;
|
||||
|
||||
h2 {
|
||||
color: $gray-30;
|
||||
@ -121,13 +121,38 @@ nav {
|
||||
}
|
||||
|
||||
li {
|
||||
font: 16px $body;
|
||||
font: 15px $body;
|
||||
color: $gray-30;
|
||||
list-style-type: none;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
nav.small {
|
||||
// Only show the mobile navigation on small screens.
|
||||
display: none;
|
||||
|
||||
float: none;
|
||||
width: 100%;
|
||||
|
||||
padding: 16px 0 0 0;
|
||||
margin: 0;
|
||||
background: $gray-10;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
border-collapse: separate;
|
||||
border-spacing: 16px 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 16px 0 0 0;
|
||||
padding: 0 0 1px 0;
|
||||
border-bottom: solid 1px $gray-20;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-top: 30px;
|
||||
font: 500 36px/60px $header;
|
||||
@ -235,8 +260,9 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
// link hsl(200, 60%, 50%);
|
||||
// hsl(210, 80%, 60%);
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
// Syntax highlighting.
|
||||
.codehilite pre {
|
||||
@ -432,21 +458,9 @@ table.precedence {
|
||||
@media only screen and (max-width: 639px) {
|
||||
.page { width: 100%; }
|
||||
|
||||
nav {
|
||||
float: none;
|
||||
width: 100%;
|
||||
padding: 10px 10px;
|
||||
margin: 0;
|
||||
background: $gray-10;
|
||||
text-align: center;
|
||||
|
||||
section {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
// Switch to the mobile navigation.
|
||||
nav.big { display: none; }
|
||||
nav.small { display: block; }
|
||||
|
||||
.main-column {
|
||||
padding: 0 20px;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
^title Syntax
|
||||
^category language
|
||||
^category guide
|
||||
|
||||
Wren's syntax is designed to be familiar to people coming from C-like languages
|
||||
while being a bit simpler and more streamlined.
|
||||
@ -37,8 +37,8 @@ even if the code already contains block comments.
|
||||
|
||||
## Reserved words
|
||||
|
||||
Some people like to see all of the reserved words in a programming language in
|
||||
one lump. If you're one of those folks, here you go:
|
||||
One way to get a quick feel for a language's style is to see what words it
|
||||
reserves. Here's what Wren has:
|
||||
|
||||
:::wren
|
||||
break class construct else false for foreign if import
|
||||
@ -124,3 +124,6 @@ same as doing:
|
||||
{
|
||||
return "single expression"
|
||||
}
|
||||
|
||||
<a class="right" href="values.html">Values →</a>
|
||||
<a href="getting-started.html">← Getting Started</a>
|
||||
@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<title>{title} – Wren</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
@ -19,30 +19,27 @@
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav>
|
||||
<nav class="big">
|
||||
<ul>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="community.html">Community</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>language</h2>
|
||||
<h2>language guide</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="expressions.html">Expressions</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modules.html">Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>types</h2>
|
||||
<ul>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="fibers.html">Fibers</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modules.html">Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
@ -51,12 +48,55 @@
|
||||
<li><a href="core">Core Library</a></li>
|
||||
<li><a href="embedding-api.html">Embedding API</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="community.html">Community</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="grammar.html">Grammar</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="getting-started.html">Getting Started</a></td>
|
||||
<td><a href="contributing.html">Contributing</a></td>
|
||||
<td><a href="community.html">Community</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>language guide</h2></td>
|
||||
<td><h2>reference</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modules.html">Modules</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="core">Core Library</a></li>
|
||||
<li><a href="embedding-api.html">Embedding API</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="grammar.html">Grammar</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>{title}</h1>
|
||||
{html}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
^title Values
|
||||
^category types
|
||||
^category guide
|
||||
|
||||
Values are the built-in object types that all other objects are composed of.
|
||||
They can be created through *literals*, expressions that evaluate to a value.
|
||||
All values are *immutable*—once created, they do not change. The number
|
||||
`3` is always the number `3`. The string `"frozen"` can never have its
|
||||
Values are the built-in atomic object types that all other objects are composed
|
||||
of. They can be created through *literals*, expressions that evaluate to a
|
||||
value. All values are *immutable*—once created, they do not change. The
|
||||
number `3` is always the number `3`. The string `"frozen"` can never have its
|
||||
character array modified in place.
|
||||
|
||||
## Booleans
|
||||
@ -59,6 +59,12 @@ A `\u` followed by four hex digits can be used to specify a Unicode code point:
|
||||
:::wren
|
||||
System.print("\u0041\u0b83\u00DE") //> AஃÞ
|
||||
|
||||
A capital `\U` followed by *eight* hex digits allows Unicode code points outside
|
||||
of the basic multilingual plane, like all-important emoji:
|
||||
|
||||
:::wren
|
||||
System.print("\U0001F64A\U0001F680") //> 🙊🚀
|
||||
|
||||
A `\x` followed by two hex digits specifies a single unencoded byte:
|
||||
|
||||
:::wren
|
||||
@ -68,10 +74,11 @@ Strings are instances of class [String](core/string.html).
|
||||
|
||||
## Ranges
|
||||
|
||||
A range is a little object that represents a consecutive range of numbers.
|
||||
They don't have their own dedicated literal syntax. Instead, the number class
|
||||
implements the `..` and `...` [operators](expressions.html#operators) to create
|
||||
them:
|
||||
A range is a little object that represents a consecutive range of numbers. They
|
||||
don't have their own dedicated literal syntax. Instead, the number class
|
||||
implements the `..` and `...` [operators][] to create them:
|
||||
|
||||
[operators]: method-calls.html#operators
|
||||
|
||||
:::wren
|
||||
3..8
|
||||
@ -102,3 +109,6 @@ Wren has a special value `null`, which is the only instance of the class
|
||||
`void` in some languages: it indicates the absence of a value. If you call a
|
||||
method that doesn't return anything and get its returned value, you get `null`
|
||||
back.
|
||||
|
||||
<a class="right" href="lists.html">Lists →</a>
|
||||
<a href="syntax.html">← Syntax</a>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
^title Variables
|
||||
^category language
|
||||
^category guide
|
||||
|
||||
Variables are named slots for storing values. You define a new variable in Wren
|
||||
using a `var` statement, like so:
|
||||
@ -67,4 +67,12 @@ assigned value.
|
||||
var a = "before"
|
||||
System.print(a = "after") //> after
|
||||
|
||||
If the left-hand side is some more complex expression than a bare variable name,
|
||||
then it isn't an assignment. Instead, it's calling a [setter method][].
|
||||
|
||||
[setter method]: method-calls.html#setters
|
||||
|
||||
**TODO: Top-level names.**
|
||||
|
||||
<a class="right" href="functions.html">Functions →</a>
|
||||
<a href="control-flow.html">← Control Flow</a>
|
||||
|
||||
@ -21,12 +21,16 @@ MARKDOWN_HEADER = re.compile(r'#+ ')
|
||||
# Clean up a header to be a valid URL.
|
||||
FORMAT_ANCHOR = re.compile(r'\.|\?|!|:|/|\*')
|
||||
|
||||
with codecs.open("doc/site/template.html", encoding="utf-8") as f:
|
||||
template = f.read()
|
||||
def load_template():
|
||||
global template
|
||||
with codecs.open("doc/site/template.html", encoding="utf-8") as f:
|
||||
template = f.read()
|
||||
|
||||
|
||||
with codecs.open("doc/site/template-core.html", encoding="utf-8") as f:
|
||||
template_core = f.read()
|
||||
def load_core_template():
|
||||
global template_core
|
||||
with codecs.open("doc/site/template-core.html", encoding="utf-8") as f:
|
||||
template_core = f.read()
|
||||
|
||||
|
||||
def ensure_dir(path):
|
||||
@ -35,15 +39,27 @@ def ensure_dir(path):
|
||||
|
||||
|
||||
def is_up_to_date(path, out_path):
|
||||
# See if it's up to date.
|
||||
source_mod = os.path.getmtime(path)
|
||||
source_mod = max(source_mod, os.path.getmtime('doc/site/template.html'))
|
||||
|
||||
dest_mod = 0
|
||||
if os.path.exists(out_path):
|
||||
dest_mod = os.path.getmtime(out_path)
|
||||
|
||||
# See if the templates have changed.
|
||||
source_mod = os.path.getmtime('doc/site/template.html')
|
||||
if source_mod > dest_mod:
|
||||
load_template()
|
||||
return False
|
||||
|
||||
# See if the templates have changed.
|
||||
source_mod = os.path.getmtime('doc/site/template-core.html')
|
||||
if source_mod > dest_mod:
|
||||
load_core_template()
|
||||
return False
|
||||
|
||||
# See if it's up to date.
|
||||
source_mod = os.path.getmtime(path)
|
||||
return source_mod < dest_mod
|
||||
|
||||
|
||||
def format_file(path, skip_up_to_date):
|
||||
basename = os.path.basename(path)
|
||||
basename = basename.split('.')[0]
|
||||
@ -89,7 +105,16 @@ def format_file(path, skip_up_to_date):
|
||||
contents += '{1} <a href="#{0}" name="{0}" class="header-anchor">#</a>\n'.format(anchor, header)
|
||||
|
||||
else:
|
||||
contents = contents + line
|
||||
# Forcibly add a space to the end of each line. Works around a bug in
|
||||
# the smartypants extension that removes some newlines that are needed.
|
||||
# https://github.com/waylan/Python-Markdown/issues/439
|
||||
if "//" not in line:
|
||||
contents = contents + line.rstrip() + ' \n'
|
||||
else:
|
||||
# Don't add a trailing space on comment lines since they may be
|
||||
# output lines which have a trailing ">" which makes the extra space
|
||||
# visible.
|
||||
contents += line
|
||||
|
||||
html = markdown.markdown(contents, ['def_list', 'codehilite', 'smarty'])
|
||||
|
||||
@ -148,6 +173,9 @@ if os.path.exists("build/docs"):
|
||||
shutil.rmtree("build/docs")
|
||||
ensure_dir("build/docs")
|
||||
|
||||
load_template()
|
||||
load_core_template()
|
||||
|
||||
# Process each markdown file.
|
||||
format_files(False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user