forked from Mirror/wren
More work on docs.
This commit is contained in:
74
doc/site/method-calls.markdown
Normal file
74
doc/site/method-calls.markdown
Normal file
@ -0,0 +1,74 @@
|
||||
^title Method Calls
|
||||
|
||||
Wren is object-oriented, so most code consists of method calls. They look like
|
||||
this:
|
||||
|
||||
:::wren
|
||||
io.write("hello")
|
||||
items.add("another")
|
||||
items.insert(1, "value")
|
||||
|
||||
You have a *receiver* on the left, followed by a `.`, then a name and an argument list in parentheses. Semantically, a method call works like this:
|
||||
|
||||
1. Look up the class of the receiver.
|
||||
2. Look up the method on it by name.
|
||||
3. Invoke the method.
|
||||
|
||||
Methods that do not take any arguments leave off the `()`:
|
||||
|
||||
:::wren
|
||||
text.length
|
||||
|
||||
These are special "getters" or "accessors" in other languages. In Wren, they're
|
||||
just methods.
|
||||
|
||||
## Arity
|
||||
|
||||
Unlike most dynamic languages, the number of arguments to a method is part of
|
||||
its call signature. Methods with different signatures are distinct from each
|
||||
other. In technical terms, this means you can overload by *arity*.
|
||||
|
||||
In normal human terms, it means you can overload by number of parameters. These
|
||||
are calls to two different methods:
|
||||
|
||||
items.add("one arg")
|
||||
items.add("first", "second")
|
||||
|
||||
Instead of having a single `add` method where you have to check for "undefined"
|
||||
or missing arguments, Wren just treats them as different methods that you can
|
||||
implement separately.
|
||||
|
||||
## Prefix Operators
|
||||
|
||||
Wren has mostly the same operators you know and love from C, with the same
|
||||
precedence and associativity. These operators are prefix (they come before
|
||||
their operand):
|
||||
|
||||
:::wren
|
||||
! ~ -
|
||||
|
||||
Semantically, these operators are just method calls on their operand. An
|
||||
expression like `!possible` just means "call the `!` on `possible`".
|
||||
|
||||
### Infix Operators
|
||||
|
||||
These operators are infix (they have operands on either side):
|
||||
|
||||
:::wren
|
||||
== !=
|
||||
< > <= >=
|
||||
| &
|
||||
+ -
|
||||
* / %
|
||||
|
||||
Like prefix operators, they are just a funny way of writing a method call. 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`".
|
||||
|
||||
### The `is` operator
|
||||
|
||||
The `is` keyword can be used as an infix operator in expression. 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).
|
||||
@ -11,9 +11,10 @@ Languages come in four rough performance buckets, from slowest to fastest:
|
||||
3. JIT compilers for dynamically typed languages: Modern JavaScript VMs,
|
||||
LuaJIT, PyPy, some Lisp/Scheme implementations.
|
||||
|
||||
4. Statically typed languages: C, C++, Java, C#, Haskell, etc.
|
||||
4. Statically compiled statically typed languages: C, C++, Java, C#, Haskell,
|
||||
etc.
|
||||
|
||||
Most languages in the first bucket aren't suitable for real-world use. (Servers are one exception, because you can always throw more hardware at a slow language there.) Languages in the second bucket are slower than the last two buckets, but are fast enough for production use, even on client hardware, as the success of the listed languages shows. Languages in the third bucket are quite fast, but their implementations are breathtakingly complex, often rivalling that of compilers for statically-typed languages.
|
||||
Most languages in the first bucket aren't suitable for production use. (Servers are one exception, because you can always throw more hardware at a slow language there.) Languages in the second bucket are fast enough for many use cases, even on client hardware, as the success of the listed languages shows. Languages in the third bucket are quite fast, but their implementations are breathtakingly complex, often rivalling that of compilers for statically-typed languages.
|
||||
|
||||
## Why is Wren fast?
|
||||
|
||||
@ -23,19 +24,21 @@ There are a few things Wren has to give it a leg up:
|
||||
|
||||
### A compact value representation
|
||||
|
||||
A core piece of a dynamic language implementation is the data structure used for variables. It needs to be able to store (or reference) a value of any type, while also being as compact as possible. Wren uses a technique called *NaN tagging* for this.
|
||||
A core piece of a dynamic language implementation is the data structure used for variables. It needs to be able to store (or reference) a value of any type, while also being as compact as possible. Wren uses a technique called *[NaN tagging][]* for this.
|
||||
|
||||
[nan tagging]: http://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations
|
||||
|
||||
All values are stored internally in Wren as small, eight byte double-precision floats. Since that is also Wren's number type, in order to do arithmetic, no conversion is needed before the "raw" number can be accessed: a value holding a number *is* a valid double. This keeps arithmetic fast.
|
||||
|
||||
To store values of other types, it turns out there's a ton of unused bits in a "NaN" double. There's room in there for a pointer as well as some other stuff. For simple values like `true`, `false`, and `null`, Wren uses special bit patterns and stores them directly in the value. For other objects like strings that are heap allocated, Wren stores the pointer in there.
|
||||
|
||||
This means numbers, bools, and null are unboxed. It also means an entire value is only eight bytes. Smaller = faster when you take into account CPU caching and the cost of passing values around.
|
||||
This means numbers, bools, and null are unboxed. It also means an entire value is only eight bytes, the native word size on 64-bit machines. Smaller = faster when you take into account CPU caching and the cost of passing values around.
|
||||
|
||||
### Fixed object layout
|
||||
|
||||
Most dynamic languages treat objects as loose bags of named properties. You can freely add and remove properties from an object after you've created it. Languages like Lua and JavaScript don't even have a well-defined concept of a "type" of object.
|
||||
|
||||
Wren is strictly class-based. Every object is an instance of a class. Classes in turn have a well-defined declarative syntax, and cannot be imperatively modified. Finally, fields in Wren are always class-private: they can only be accessed from methods defined directly on that class.
|
||||
Wren is strictly class-based. Every object is an instance of a class. Classes in turn have a well-defined declarative syntax, and cannot be imperatively modified. In addition, fields in Wren are always class-private: they can only be accessed from methods defined directly on that class.
|
||||
|
||||
Put all of that together and it means you can determine at *compile* time exactly how many fields an object has and what they are. In other languages, when you create an object, you allocate some initial memory for it, but that may have to be reallocated multiple times as fields are added and the object grows. Wren just does a single allocation up front for exactly the right number of fields.
|
||||
|
||||
|
||||
@ -29,26 +29,30 @@ one lump. If you're one of those folks, here you go:
|
||||
class else false fn for if is null
|
||||
return static this true var while
|
||||
|
||||
## Newlines
|
||||
## Statement terminators
|
||||
|
||||
Like many scripting languages, newlines are significant in Wren and are used to
|
||||
separate statements. You can keep your semicolons safely tucked away.
|
||||
Officially, statements are terminated by a semicolon (`;`) like in other
|
||||
languages in the C tradition. However, Wren treats newlines as equivalent
|
||||
to a semicolon whenever it makes sense. In practice, this means you almost
|
||||
never write `;` unless you want to cram a bunch of statements on one line.
|
||||
|
||||
:::wren
|
||||
// Two statements:
|
||||
io.write("hi")
|
||||
io.write("bye")
|
||||
|
||||
Sometimes, though, you want to wrap a single statement on multiple lines. To
|
||||
make that easier, Wren has a very simple rule. It will ignore a newline
|
||||
following any token that can't end a statement. Specifically, that means any of
|
||||
these:
|
||||
Sometimes, though, a statement doesn't fit on a single line and treating the
|
||||
newline as a semicolon would trip things up. To handle that, Wren has a very
|
||||
simple rule. It ignores a newline following any token that can't end a
|
||||
statement. Specifically, that means any of these:
|
||||
|
||||
:::wren
|
||||
( [ { . , * / % + - | || & && ! ~ = < > <= >= == !=
|
||||
class else if is static var while
|
||||
|
||||
Everywhere else, a newline is treated just like a `;`.
|
||||
Everywhere else, a newline is treated just like a `;`. Note that this is a very
|
||||
different system from how JavaScript handles semicolons. If you've been burned
|
||||
there, don't worry, you should be fine here.
|
||||
|
||||
## Names
|
||||
|
||||
@ -62,60 +66,6 @@ Identifiers are similar to other programming languages. They start with a letter
|
||||
abc123
|
||||
ALL_CAPS
|
||||
|
||||
Identifiers that start with underscore (`_`) are special in Wren. They are used to indicate fields and private members of classes.
|
||||
Identifiers that start with underscore (`_`) are special in Wren. They are used to indicate fields in [classes](classes.html).
|
||||
|
||||
## Method calls
|
||||
|
||||
Wren is object-oriented, so most code consists of method calls. They look like this:
|
||||
|
||||
:::wren
|
||||
io.write("hello")
|
||||
items.add("another")
|
||||
items.insert(1, "value")
|
||||
|
||||
You have a *receiver* on the left, followed by a `.`, then a name and an argument list in parentheses. Semantically, a method call works like this:
|
||||
|
||||
1. Look up the class of the receiver.
|
||||
2. Look up the method on it by name.
|
||||
3. Invoke the method.
|
||||
|
||||
Methods that do not take any arguments leave off the `()`:
|
||||
|
||||
:::wren
|
||||
text.length
|
||||
|
||||
These are special "getters" or "accessors" in other languages. In Wren, they're just methods. Unlike most dynamic languages, the number of arguments to a method is part of its *name*. In technical terms, this means you can overload by *arity*. Basically, it means that these are calls to two different methods:
|
||||
|
||||
items.add("one arg")
|
||||
items.add("first", "second")
|
||||
|
||||
## Operators
|
||||
|
||||
Wren has mostly the same operators you know and love from C, with the same precedence and associativity. These operators are prefix (they come before their operand):
|
||||
|
||||
:::wren
|
||||
! ~ -
|
||||
|
||||
Semantically, they are just method calls on their operand. When you see `!possible`, it's effectively the same as `possible.!` (though Wren does *not* allow that syntax).
|
||||
|
||||
These operators are infix (they have operands on either side):
|
||||
|
||||
:::wren
|
||||
=
|
||||
|| &&
|
||||
is
|
||||
== !=
|
||||
< > <= >=
|
||||
| &
|
||||
+ -
|
||||
* / %
|
||||
|
||||
The `is` operator is used for type tests. 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).
|
||||
|
||||
The `||` and `&&` are logical operators. Like in C, they are basically flow-control constructs. A `||` expression will only evaluate the right operand if the left-hand side evaluates to something non-false-y. Likewise, `&&` only evaluates the right operand if the left evaluates to something false-y.
|
||||
|
||||
In Wren, the only false value is the boolean value `false`. Everything else is considered "true".
|
||||
|
||||
All other infix operators are just syntactic sugar for method calls. The left operand is the receiver, and the right is passed to it as an argument. So `a + b` is semantically `a.+(b)`. The built-in types implement these methods to do what you (hopefully) expect.
|
||||
|
||||
**TODO: assignment, functions, lists, maps, flow control, whitespace and newlines**
|
||||
**TODO: blocks, assignment, functions, lists, maps**
|
||||
|
||||
@ -25,9 +25,8 @@
|
||||
<h2>Language</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li>Method calls</li>
|
||||
<li>Variables</li>
|
||||
<li>Blocks</li>
|
||||
<li><a href="method-calls.html">Method calls</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="flow-control.html">Flow control</a></li>
|
||||
</ul>
|
||||
<h2>Types</h2>
|
||||
|
||||
67
doc/site/variables.markdown
Normal file
67
doc/site/variables.markdown
Normal file
@ -0,0 +1,67 @@
|
||||
^title Variables
|
||||
|
||||
Variables are named slots for storing values. You can define a new variable in
|
||||
Wren using a `var` statement, like so:
|
||||
|
||||
:::wren
|
||||
var a = 1 + 2
|
||||
|
||||
This creates a new variable `a` in the current scope and initializes it with
|
||||
the result of the expression following the `=`. Once a variable has been
|
||||
defined, it can be accessed by name as you would expect.
|
||||
|
||||
:::wren
|
||||
var animal = "Slow Loris"
|
||||
io.write(animal) // prints "Slow Loris"
|
||||
|
||||
## Scope
|
||||
|
||||
Wren has true block scope: a variable exists from the point where it is
|
||||
defined until the end of the block where that definition appears.
|
||||
|
||||
:::wren
|
||||
{
|
||||
io.write(a) // ERROR! a doesn't exist yet
|
||||
var a = 123
|
||||
io.write(a) // "123"
|
||||
}
|
||||
io.write(a) // ERROR! a doesn't exist anymore
|
||||
|
||||
Variables defined at the top level of a script are *global*. All other variables
|
||||
are *local*. Declaring a variable in an inner scope with the same name as an
|
||||
outer one is called *shadowing* and is not an error (although it's not
|
||||
something you likely intend to do much).
|
||||
|
||||
:::wren
|
||||
var a = "outer"
|
||||
{
|
||||
var a = "inner"
|
||||
io.write(a) // Prints "inner".
|
||||
}
|
||||
io.write(a) // Prints "outer".
|
||||
|
||||
Declaring a variable with the same name in the *same* scope *is* an error.
|
||||
|
||||
:::wren
|
||||
var a = "hi"
|
||||
var a = "again" // ERROR!
|
||||
|
||||
## Assignment
|
||||
|
||||
After a variable has been declared, you can assign to it using `=`:
|
||||
|
||||
var a = 123
|
||||
a = 234
|
||||
|
||||
An assignment walks up the scope stack to find where the named variable is
|
||||
declared. It's an error to assign to a variable that isn't defined. Wren
|
||||
doesn't roll with implicit variable definition.
|
||||
|
||||
When used in a larger expression, an assignment expression evaluates to the
|
||||
assigned value.
|
||||
|
||||
:::wren
|
||||
var a = "before"
|
||||
io.write(a = "after") // Prints "after".
|
||||
|
||||
**TODO: Forward references for globals, closures.**
|
||||
Reference in New Issue
Block a user