1
0
forked from Mirror/wren

More work on docs.

This commit is contained in:
Bob Nystrom
2013-12-17 09:39:26 -08:00
parent 271fcec81b
commit caafa96252
5 changed files with 165 additions and 72 deletions

View 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).

View File

@ -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.

View File

@ -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**

View File

@ -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>

View 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.**