diff --git a/classes.html b/classes.html index 791edb74..3ef41a1c 100644 --- a/classes.html +++ b/classes.html @@ -104,43 +104,47 @@ class supports the same methods. State is defined in fields, whose valu stored in each instance.
Classes are created using the class keyword, unsurprisingly:
class Unicorn {}
+class Unicorn {} +
This creates a class named Unicorn with no methods or fields.
To let our unicorn do stuff, we need to give it methods.
-class Unicorn {
- prance() {
- System.print("The unicorn prances in a fancy manner!")
- }
-}
+class Unicorn { + prance() { + System.print("The unicorn prances in a fancy manner!") + } +} +
This defines a prance() method that takes no arguments. To add parameters, put
their names inside the parentheses:
class Unicorn {
- prance(where, when) {
- System.print("The unicorn prances in %(where) at %(when).")
- }
-}
+class Unicorn { + prance(where, when) { + System.print("The unicorn prances in %(where) at %(when).") + } +} +
Since the number of parameters is part of a method’s signature a class can define multiple methods with the same name:
-class Unicorn {
- prance() {
- System.print("The unicorn prances in a fancy manner!")
- }
+class Unicorn {
+ prance() {
+ System.print("The unicorn prances in a fancy manner!")
+ }
- prance(where) {
- System.print("The unicorn prances in %(where).")
- }
+ prance(where) {
+ System.print("The unicorn prances in %(where).")
+ }
- prance(where, when) {
- System.print("The unicorn prances in %(where) at %(when).")
- }
-}
+ prance(where, when) {
+ System.print("The unicorn prances in %(where) at %(when).")
+ }
+}
+
It’s often natural to have the same conceptual operation work with different
@@ -151,63 +155,69 @@ different methods that you implement separately.
different syntaxes for methods. Your classes can define all of them.
Getters #
A getter leaves off the parameter list and the parentheses:
-class Unicorn {
- // Unicorns are always fancy.
- isFancy { true }
-}
+class Unicorn {
+ // Unicorns are always fancy.
+ isFancy { true }
+}
+
Setters #
A setter has = after the name, followed by a single parenthesized parameter:
-class Unicorn {
- rider=(value) {
- System.print("I am being ridden by %(value).")
- }
-}
+class Unicorn {
+ rider=(value) {
+ System.print("I am being ridden by %(value).")
+ }
+}
+
By convention, the parameter is usually named value but you can call it
whatever makes your heart flutter.
Operators #
Prefix operators, like getters, have no parameter list:
-class Unicorn {
- - {
- System.print("Negating a unicorn is weird.")
- }
-}
+class Unicorn {
+ - {
+ System.print("Negating a unicorn is weird.")
+ }
+}
+
Infix operators, like setters, have a single parenthesized parameter for the
right-hand operand:
-class Unicorn {
- -(other) {
- System.print("Subtracting %(other) from a unicorn is weird.")
- }
-}
+class Unicorn {
+ -(other) {
+ System.print("Subtracting %(other) from a unicorn is weird.")
+ }
+}
+
A subscript operator puts the parameters inside square brackets and can have
more than one:
-class Unicorn {
- [index] {
- System.print("Unicorns are not lists!")
- }
+class Unicorn {
+ [index] {
+ System.print("Unicorns are not lists!")
+ }
- [x, y] {
- System.print("Unicorns are not matrices either!")
- }
-}
+ [x, y] {
+ System.print("Unicorns are not matrices either!")
+ }
+}
+
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:
-class Unicorn {
- [index]=(value) {
- System.print("You can't stuff %(value) into me at %(index)!")
- }
-}
+class Unicorn {
+ [index]=(value) {
+ System.print("You can't stuff %(value) into me at %(index)!")
+ }
+}
+
Method Scope #
@@ -216,24 +226,26 @@ operator and a setter:
that’s the only kind of scope there is. But object-oriented languages like Wren
introduce another kind of scope: object scope. It contains the methods that
are available on an object. When you write:
-unicorn.isFancy
+unicorn.isFancy
+
You’re saying “look up the method isFancy in the scope of the object
-unicorn“. In this case, the fact that you want to look up a method isFancy
+unicorn”. In this case, the fact that you want to look up a method isFancy
and not a variable isFancy is explicit. That’s what . does and the
object to the left of the period is the object you want to look up the method on.
this #
Things get more interesting when you’re inside the body of a method. When the
method is called on some object and the body is being executed, you often need
to access that object itself. You can do that using this.
-class Unicorn {
- name { "Francis" }
+class Unicorn {
+ name { "Francis" }
- printName() {
- System.print(this.name) //> Francis
- }
-}
+ printName() {
+ System.print(this.name) Francis
+ }
+}
+
The this keyword works sort of like a variable, but has special behavior. It
@@ -242,16 +254,17 @@ 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 declared inside a method. When you do,
this still refers to the instance whose method is being called:
-class Unicorn {
- name { "Francis" }
+class Unicorn {
+ name { "Francis" }
- printNameThrice() {
- (1..3).each {
- // Use "this" inside the function passed to each().
- System.print(this.name) //> Francis
- } //> Francis
- } //> Francis
-}
+ printNameThrice() {
+ (1..3).each {
+ // Use "this" inside the function passed to each().
+ System.print(this.name) Francis
+ } Francis
+ } Francis
+}
+
This is unlike Lua and JavaScript which can “forget” this when you create a
@@ -264,26 +277,28 @@ because it makes a distinction between methods and functions.)
tedious and verbose, which is why some languages don’t require it. You can do a
“self send” by calling a method (or getter or setter) without any explicit
receiver:
-class Unicorn {
- name { "Francis" }
+class Unicorn {
+ name { "Francis" }
- printName() {
- System.print(name) //> Francis
- }
-}
+ printName() {
+ System.print(name) Francis
+ }
+}
+
Code like this gets tricky when there is also a variable outside of the class
with the same name. Consider:
-var name = "variable"
+var name = "variable"
-class Unicorn {
- name { "Francis" }
+class Unicorn {
+ name { "Francis" }
- printName() {
- System.print(name) // ???
- }
-}
+ printName() {
+ System.print(name) // ???
+ }
+}
+
Should printName() print “variable” or “Francis”? A method body has a foot in
@@ -306,23 +321,24 @@ lowercase in Wren. Class names are capitalized.
of the class, it’s usually the name of some other class. This rule makes that
work.
Here’s an example that shows all three cases:
-var shadowed = "surrounding"
-var lowercase = "surrounding"
-var Capitalized = "surrounding"
+var shadowed = "surrounding"
+var lowercase = "surrounding"
+var Capitalized = "surrounding"
-class Scope {
- shadowed { "object" }
- lowercase { "object" }
- Capitalized { "object" }
+class Scope {
+ shadowed { "object" }
+ lowercase { "object" }
+ Capitalized { "object" }
- test() {
- var shadowed = "local"
+ test() {
+ var shadowed = "local"
- System.print(shadowed) //> local
- System.print(lowercase) //> object
- System.print(Capitalized) //> surrounding
- }
-}
+ System.print(shadowed) local
+ System.print(lowercase) object
+ System.print(Capitalized) surrounding
+ }
+}
+
It’s a bit of a strange rule, but Ruby works more or less the same way.
@@ -331,29 +347,32 @@ class Scope {
Our unicorns can prance around, but we don’t actually have any unicorns to do
it. To create instances of a class, we need a constructor. You define one
like so:
-class Unicorn {
- construct new(name, color) {
- System.print("My name is " + name + " and I am " + color + ".")
- }
-}
+class Unicorn {
+ construct new(name, color) {
+ System.print("My name is " + name + " and I am " + color + ".")
+ }
+}
+
The construct keyword says we’re defining a constructor, and new is its
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 call the constructor method on the class itself:
-var fred = Unicorn.new("Fred", "palomino")
+var fred = Unicorn.new("Fred", "palomino")
+
Giving constructors names is handy because it means you can have more than one,
and each can clarify how it creates the instance:
-class Unicorn {
- construct brown(name) {
- System.print("My name is " + name + " and I am brown.")
- }
-}
+class Unicorn {
+ construct brown(name) {
+ System.print("My name is " + name + " and I am brown.")
+ }
+}
-var dave = Unicorn.brown("Dave")
+var dave = Unicorn.brown("Dave")
+
Note that we have to declare a constructor because, unlike some other
@@ -366,7 +385,8 @@ overloaded by arity. A constructor must be a n
a (possibly empty) argument list. Operators, getters, and setters cannot be
constructors.
A constructor is actually a pair of methods. You get a method on the class:
-Unicorn.brown("Dave")
+Unicorn.brown("Dave")
+
That creates the new instance, then it invokes the initializer on that
@@ -377,11 +397,12 @@ constructors, etc.
Fields #
All state stored in instances is stored in fields. Each field has a name
that starts with an underscore.
-class Rectangle {
- area { _width * _height }
+class Rectangle {
+ area { _width * _height }
- // Other stuff...
-}
+ // Other stuff...
+}
+
Here, _width and _height in the area getter refer
@@ -401,19 +422,21 @@ accessed from within methods defined on the object’s class. You cannot eve
access fields on another instance of your own class, unlike C++ and Java.
If you want to make a property of an object visible, you need to define a
getter to expose it:
-class Rectangle {
- width { _width }
- height { _height }
+class Rectangle {
+ width { _width }
+ height { _height }
- // ...
-}
+ // ...
+}
+
To allow outside code to modify the field, you’ll also need to provide setters:
-class Rectangle {
- width=(value) { _width = value }
- height=(value) { _height = value }
-}
+class Rectangle {
+ width=(value) { _width = value }
+ height=(value) { _height = value }
+}
+
One thing we’ve learned in the past forty years of software engineering is that
@@ -426,38 +449,42 @@ to or even should define getters or setters for most of your object’s fiel
A name that starts with two underscores is a static field. They work
similar to fields except the data is stored on the class itself, and
not the instance. They can be used in both instance and static methods.
-class Foo {
- construct new() {}
+class Foo {
+ construct new() {}
- static setFromStatic(a) { __a = a }
- setFromInstance(a) { __a = a }
+ static setFromStatic(a) { __a = a }
+ setFromInstance(a) { __a = a }
- static printFromStatic() {
- System.print(__a)
- }
+ static printFromStatic() {
+ System.print(__a)
+ }
- printFromInstance() {
- System.print(__a)
- }
-}
+ printFromInstance() {
+ System.print(__a)
+ }
+}
+
Just like instance fields, static fields are initially null:
-Foo.printFromStatic() //> null
+Foo.printFromStatic() //> null
+
They can be used from static methods:
-Foo.setFromStatic("first")
-Foo.printFromStatic() //> first
+Foo.setFromStatic("first")
+Foo.printFromStatic() //> first
+
And also instance methods. When you do so, there is still only one static field
shared among all instances of the class:
-var foo1 = Foo.new()
+var foo1 = Foo.new()
var foo2 = Foo.new()
foo1.setFromInstance("second")
-foo2.printFromInstance() //> second
+foo2.printFromInstance() //> second
+
Inheritance #
@@ -467,7 +494,8 @@ superclasses looking for it there.
By default, any new class inherits from Object, which is the superclass from
which all other classes ultimately descend. You can specify a different parent
class using is when you declare the class:
-class Pegasus is Unicorn {}
+class Pegasus is Unicorn {}
+
This declares a new class Pegasus that inherits from Unicorn.
@@ -479,45 +507,48 @@ of the inherited built-in methods on the derived type.
Pegasus inherits from Unicorn, Pegasus’s metaclass does not inherit from
Unicorn’s metaclass. In more prosaic terms, this means that static methods are
not inherited.
-class Unicorn {
- // Unicorns cannot fly. :(
- static canFly { false }
-}
+class Unicorn {
+ // Unicorns cannot fly. :(
+ static canFly { false }
+}
-class Pegasus is Unicorn {}
+class Pegasus is Unicorn {}
-Pegasus.canFly //! Static methods are not inherited.
+Pegasus.canFly Static methods are not inherited.
+
This also means constructors are not inherited:
-class Unicorn {
- construct new(name) {
- System.print("My name is " + name + ".")
- }
-}
+class Unicorn {
+ construct new(name) {
+ System.print("My name is " + name + ".")
+ }
+}
-class Pegasus is Unicorn {}
+class Pegasus is Unicorn {}
-Pegasus.new("Fred") //! Pegasus does not define new().
+Pegasus.new("Fred") Pegasus does not define new().
+
Each class gets to control how it may be constructed independently of its base
classes. However, constructor initializers are inherited since those are
instance methods on the new object.
This means you can do super calls inside a constructor:
-class Unicorn {
- construct new(name) {
- System.print("My name is " + name + ".")
- }
-}
+class Unicorn {
+ construct new(name) {
+ System.print("My name is " + name + ".")
+ }
+}
-class Pegasus is Unicorn {
- construct new(name) {
- super(name)
- }
-}
+class Pegasus is Unicorn {
+ construct new(name) {
+ super(name)
+ }
+}
-Pegasus.new("Fred") //> My name is Fred
+Pegasus.new("Fred") My name is Fred
+
Super #
@@ -529,32 +560,34 @@ 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:
-class Base {
- method() {
- System.print("base method")
- }
-}
+class Base {
+ method() {
+ System.print("base method")
+ }
+}
-class Derived is Base {
- method() {
- super.method() //> 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:
-class Base {
- construct new(arg) {
- System.print("base got " + arg)
- }
-}
+class Base {
+ construct new(arg) {
+ System.print("base got " + arg)
+ }
+}
-class Derived is Base {
- construct new() {
- super("value") //> base got value
- }
-}
+class Derived is Base {
+ construct new() {
+ super("value") base got value
+ }
+}
+
Concurrency →
diff --git a/concurrency.html b/concurrency.html
index 2678ad34..92019d48 100644
--- a/concurrency.html
+++ b/concurrency.html
@@ -114,9 +114,10 @@ fiber for every line of code you type in.
All Wren code runs within the context of a fiber. When you first start a Wren
script, a main fiber is created for you automatically. You can spawn new fibers
using the Fiber class’s constructor:
-var fiber = Fiber.new {
+var fiber = Fiber.new {
System.print("This runs in a separate fiber.")
-}
+}
+
It takes a function containing the code the fiber should execute. The
@@ -125,19 +126,21 @@ fiber does not immediately run it. It just wraps the function and sits there,
waiting to be activated.
Invoking fibers #
Once you’ve created a fiber, you run it by calling its call() method:
-fiber.call()
+fiber.call()
+
This suspends the current fiber and executes the called one until it reaches the
end of its body or until it passes control to yet another fiber. If it reaches
the end of its body, it is considered done:
-var fiber = Fiber.new {
- System.print("It's alive!")
+var fiber = Fiber.new {
+ System.print("It's alive!")
}
System.print(fiber.isDone) //> false
-fiber.call() //> It's alive!
-System.print(fiber.isDone) //> true
+fiber.call() //> It's alive!
+System.print(fiber.isDone) //> true
+
When a called fiber finishes, it automatically passes control back to the
@@ -152,7 +155,7 @@ as one function calling another.
back to the fiber that ran it, but remembers where it is. The next time the
fiber is called, it picks up right where it left off and keeps going.
You make a fiber yield by calling the static yield() method on Fiber:
-var fiber = Fiber.new {
+var fiber = Fiber.new {
System.print("Before yield")
Fiber.yield()
System.print("Resumed")
@@ -162,7 +165,8 @@ System.print("Before call") //> Before call
fiber.call() //> Before yield
System.print("Calling again") //> Calling again
fiber.call() //> Resumed
-System.print("All done") //> All done
+System.print("All done") //> All done
+
Note that even though this program uses concurrency, it is still
@@ -173,33 +177,36 @@ the mercy of a thread scheduler playing Russian roulette with your code.
data. When you call a fiber, you can optionally pass a value to it.
If you create a fiber using a function that takes a parameter, you can pass a
value to it through call():
-var fiber = Fiber.new {|param|
+var fiber = Fiber.new {|param|
System.print(param)
}
-fiber.call("Here you go") //> Here you go
+fiber.call("Here you go") //> Here you go
+
If the fiber has yielded and is waiting to resume, the value you pass to call
becomes the return value of the yield() call when it resumes:
-var fiber = Fiber.new {|param|
+var fiber = Fiber.new {|param|
System.print(param)
var result = Fiber.yield()
System.print(result)
}
fiber.call("First") //> First
-fiber.call("Second") //> Second
+fiber.call("Second") //> Second
+
Fibers can also pass values back when they yield. If you pass an argument to
yield(), that will become the return value of the call() that was used to
invoke the fiber:
-var fiber = Fiber.new {
+var fiber = Fiber.new {
Fiber.yield("Reply")
}
-System.print(fiber.call()) //> Reply
+System.print(fiber.call()) //> Reply
+
This is sort of like how a function call may return a value, except that a fiber
@@ -214,11 +221,12 @@ you can iterate over.
you use to create a fiber can call a method that calls another method that calls
some third method which finally calls yield. When that happens, all of those
method calls — the entire callstack — gets suspended. For example:
-var fiber = Fiber.new {
+var fiber = Fiber.new {
(1..10).each {|i|
Fiber.yield(i)
}
-}
+}
+
Here, we’re calling yield() from within a function being
diff --git a/contributing.html b/contributing.html
index a5e56153..5172761b 100644
--- a/contributing.html
+++ b/contributing.html
@@ -134,25 +134,29 @@ written in Markdown (
simple Python script, util/generate_docs.py, converts that to HTML and CSS.
The site uses Pygments for syntax highlighting, with a custom lexer plug-in
for Wren. To install that, run:
-$ cd util/pygments-lexer
+$ cd util/pygments-lexer
$ sudo python setup.py develop
-$ cd ../.. # Back to the root Wren directory.
+$ cd ../.. # Back to the root Wren directory.
+
Now you can build the docs:
-$ make docs
+$ make docs
+
This generates the site in build/docs/. You can run any simple static web
server from there. Python includes one:
-$ cd build/docs
-$ python -m SimpleHTTPServer
+$ cd build/docs
+$ python -m SimpleHTTPServer
+
Running make docs is a drag every time you change a line of Markdown or SASS,
so there is also a file watching version that will automatically regenerate the
docs when you edit a file:
-$ make watchdocs
+$ make watchdocs
+
Hacking on the VM #
@@ -162,7 +166,8 @@ docs when you edit a file:
Make sure you can build and run the tests locally. It’s good to ensure
you’re starting from a happy place before you poke at the code. Running the
tests is as simple as:
-$ make test
+$ make test
+
If there are no failures, you’re good to go.
diff --git a/control-flow.html b/control-flow.html
index 0823cb75..0c48cd4c 100644
--- a/control-flow.html
+++ b/control-flow.html
@@ -118,29 +118,33 @@ values.
If statements #
The simplest branching statement, if lets you conditionally skip a chunk of
code. It looks like this:
-if (ready) System.print("go!")
+if (ready) System.print("go!")
+
That evaluates the parenthesized expression after if. If it’s true, then the
statement after the condition is evaluated. Otherwise it is skipped. Instead of
a statement, you can have a block:
-if (ready) {
+if (ready) {
System.print("getSet")
System.print("go!")
-}
+}
+
You may also provide an else branch. It will be executed if the condition is
false:
-if (ready) System.print("go!") else System.print("not ready!")
+if (ready) System.print("go!") else System.print("not ready!")
+
And, of course, it can take a block too:
-if (ready) {
+if (ready) {
System.print("go!")
} else {
System.print("not ready!")
-}
+}
+
Logical operators #
@@ -150,22 +154,25 @@ only conditionally evaluate right operand—they short-circuit.
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.
-System.print(false && 1) //> false
-System.print(1 && 2) //> 2
+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:
-System.print(false || 1) //> 1
-System.print(1 || 2) //> 1
+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.
-System.print(1 != 2 ? "math is sane" : "math is not sane!")
+System.print(1 != 2 ? "math is sane" : "math is not sane!")
+
It takes a condition expression, followed by ?, followed by a then
@@ -178,7 +185,7 @@ repeatedly. To do that, you use looping statements. There are two in Wren, and
they should be familiar if you’ve used other imperative languages.
The simplest, a while statement executes a chunk of code as long as a
condition continues to hold. For example:
-// Hailstone sequence.
+// Hailstone sequence.
var n = 27
while (n != 1) {
if (n % 2 == 0) {
@@ -186,7 +193,8 @@ while (n != 1) {
} else {
n = 3 * n + 1
}
-}
+}
+
This evaluates the expression n != 1. If it is true, then it executes the
@@ -196,8 +204,9 @@ something true.
The condition for a while loop can be any expression, and must be surrounded by
parentheses. The body of the loop is usually a curly block but can also be a
single statement:
-var n = 27
-while (n != 1) if (n % 2 == 0) n = n / 2 else n = 3 * n + 1
+var n = 27
+while (n != 1) if (n % 2 == 0) n = n / 2 else n = 3 * n + 1
+
For statements #
@@ -205,9 +214,10 @@ while (n != 1) if (n % 2 == 0) n = n / 2 else n = 3 * n + 1
some complex condition. But in most cases, you’re looping through
a list, a series of numbers, or some other “sequence” object.
That’s what for is, uh, for. It looks like this:
-for (beatle in ["george", "john", "paul", "ringo"]) {
+for (beatle in ["george", "john", "paul", "ringo"]) {
System.print(beatle)
-}
+}
+
A for loop has three components:
@@ -231,26 +241,29 @@ That’s what for is, uh, for. It looks like this:
and stop. To do that, you can use a break statement. It’s just the break
keyword all by itself. That immediately exits out of the nearest enclosing
while or for loop.
-for (i in [1, 2, 3, 4]) {
+for (i in [1, 2, 3, 4]) {
System.print(i) //> 1
if (i == 3) break //> 2
-} //> 3
+} //> 3
+
Numeric ranges #
Lists are one common use for for loops, but sometimes you want to walk over a
sequence of numbers, or loop a number of times. For that, you can create a
range, like so:
-for (i in 1..100) {
+for (i in 1..100) {
System.print(i)
-}
+}
+
This loops over the numbers from 1 to 100, including 100 itself. If you want to
leave off the last value, use three dots instead of two:
-for (i in 1...100) {
+for (i in 1...100) {
System.print(i)
-}
+}
+
This looks like some special “range” syntax in the for loop, but it’s actually
@@ -265,18 +278,20 @@ are defined in terms of an “iterator protocol”. The loop itself does
anything about lists or ranges, it just knows how to call two particular
methods on the object that resulted from evaluating the sequence expression.
When you write a loop like this:
-for (i in 1..100) {
+for (i in 1..100) {
System.print(i)
-}
+}
+
Wren sees it something like this:
-var iter_ = null
+var iter_ = null
var seq_ = 1..100
while (iter_ = seq_.iterate(iter_)) {
var i = seq_.iteratorValue(iter_)
System.print(i)
-}
+}
+
First, Wren evaluates the sequence expression and stores it in a hidden
diff --git a/embedding/calling-c-from-wren.html b/embedding/calling-c-from-wren.html
index 301a1231..11e56c56 100644
--- a/embedding/calling-c-from-wren.html
+++ b/embedding/calling-c-from-wren.html
@@ -78,9 +78,10 @@ define foreign classes. This page is about the first, foreign m
class, it has a name and signature, and calls to it are dynamically dispatched.
The only difference is that the body of the method is written in C.
A foreign method is declared in Wren like so:
-class Math {
- foreign static add(a, b)
-}
+class Math {
+ foreign static add(a, b)
+}
+
The foreign keyword tells Wren that the method add() is declared on Math,
@@ -95,45 +96,48 @@ function that should be used for the foreign method.
first configure the VM. This callback isn’t the foreign method itself.
It’s the binding function your app uses to look up foreign methods.
Its signature is:
-WrenForeignMethodFn bindForeignMethodFn(
- WrenVM* vm,
- const char* module,
- const char* className,
- bool isStatic,
- const char* signature);
+WrenForeignMethodFn bindForeignMethodFn(
+ WrenVM* vm,
+ const char* module,
+ const char* className,
+ bool isStatic,
+ const char* signature);
+
Every time a foreign method is first declared, the VM invokes this callback. It
passes in the module containing the class declaration, the name of the class
containing the method, the method’s signature, and whether or not it’s a static
method. In the above example, it would pass something like:
-bindForeignMethodFn(vm, "main", "Math", true, "add(_,_)");
+bindForeignMethodFn(vm, "main", "Math", true, "add(_,_)");
+
When you configure the VM, you give it a C callback that looks up the
appropriate function for the given foreign method and returns a pointer to it.
Something like:
-WrenForeignMethodFn bindForeignMethod(
- WrenVM* vm,
- const char* module,
- const char* className,
- bool isStatic,
- const char* signature)
-{
- if (strcmp(module, "main") == 0)
- {
- if (strcmp(className, "Math") == 0)
- {
- if (isStatic && strcmp(signature, "add(_,_)") == 0)
- {
- return mathAdd; // C function for Math.add(_,_).
- }
- // Other foreign methods on Math...
- }
- // Other classes in main...
- }
- // Other modules...
-}
+WrenForeignMethodFn bindForeignMethod(
+ WrenVM* vm,
+ const char* module,
+ const char* className,
+ bool isStatic,
+ const char* signature)
+{
+ if (strcmp(module, "main") == 0)
+ {
+ if (strcmp(className, "Math") == 0)
+ {
+ if (isStatic && strcmp(signature, "add(_,_)") == 0)
+ {
+ return mathAdd; // C function for Math.add(_,_).
+ }
+ // Other foreign methods on Math...
+ }
+ // Other classes in main...
+ }
+ // Other modules...
+}
+
This implementation is pretty tedious, but you get the idea. Feel free to do
@@ -144,7 +148,8 @@ first executed. It then keeps the function pointer you return and associates it
with that method. This way, calls to the foreign method are fast.
Implementing a Foreign Method #
All C functions for foreign methods have the same signature:
-void foreignMethod(WrenVM* vm);
+void foreignMethod(WrenVM* vm);
+
Arguments passed from Wren are not passed as C arguments, and the method’s
@@ -156,12 +161,13 @@ object is in slot zero, and arguments are in consecutive slots after that.
You use the slot API to read those arguments, and then perform whatever work you
want to in C. If you want the foreign method to return a value, place it in slot
zero. Like so:
-void mathAdd(WrenVM* vm)
-{
- double a = wrenGetSlotDouble(vm, 1);
- double b = wrenGetSlotDouble(vm, 2);
- wrenSetSlotDouble(vm, 0, a + b);
-}
+void mathAdd(WrenVM* vm)
+{
+ double a = wrenGetSlotDouble(vm, 1);
+ double b = wrenGetSlotDouble(vm, 2);
+ wrenSetSlotDouble(vm, 0, a + b);
+}
+
While your foreign method is executing, the VM is completely suspended. No other
diff --git a/embedding/calling-wren-from-c.html b/embedding/calling-wren-from-c.html
index 111638f7..bc8625a4 100644
--- a/embedding/calling-wren-from-c.html
+++ b/embedding/calling-wren-from-c.html
@@ -93,7 +93,8 @@ C, we need a few things:
We’ll tackle these one at a time.
Getting a Method Handle #
When you run a chunk of Wren code like this:
-object.someMethod(1, 2, 3)
+object.someMethod(1, 2, 3)
+
At runtime, the VM has to look up the class of object and find a method there
@@ -111,7 +112,8 @@ array index into the table. That’s why metho
It would be a shame if calling a method from C didn’t have that same speed
benefit. To achieve that, we split the process of calling a method into two
steps. First, we create a handle that represents a “compiled” method signature:
-WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
+WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
+
That takes a method signature as a string and gives you back an opaque handle
@@ -138,11 +140,12 @@ define a static method on an appropriate class.
engine to update all of the entities each frame. We’ll keep track of the list of
entities within Wren, so from C, there’s no obvious object to call update(_)
on. Instead, we’ll just make it a static method:
-class GameEngine {
- static update(elapsedTime) {
- // ...
- }
-}
+class GameEngine {
+ static update(elapsedTime) {
+ // ...
+ }
+}
+
Often, when you call a Wren method from C, you’ll be calling a static method.
@@ -152,23 +155,26 @@ class, you’re really declaring a variable with the class’s name and
reference to the class object in it.
Assuming you declared that class at the top level, the C API gives you a way to
look it up. We can get a handle to the above class like so:
-// Load the class into slot 0.
-wrenEnsureSlots(vm, 1);
-wrenGetVariable(vm, "main", "GameEngine", 0);
+// Load the class into slot 0.
+wrenEnsureSlots(vm, 1);
+wrenGetVariable(vm, "main", "GameEngine", 0);
+
We could do this every time we call update(), but, again, that’s kind of slow
because we’re looking up “GameEngine” by name each time. A faster solution is to
create a handle to the class once and use it each time:
-// Load the class into slot 0.
-wrenEnsureSlots(vm, 1);
-wrenGetVariable(vm, "main", "GameEngine", 0);
-WrenHandle* gameEngineClass = wrenGetSlotHandle(vm, 0);
+// Load the class into slot 0.
+wrenEnsureSlots(vm, 1);
+wrenGetVariable(vm, "main", "GameEngine", 0);
+WrenHandle* gameEngineClass = wrenGetSlotHandle(vm, 0);
+
Now, each time we want to call a method on GameEngine, we store that value back
in slot zero:
-wrenSetSlotHandle(vm, 0, gameEngineClass);
+wrenSetSlotHandle(vm, 0, gameEngineClass);
+
Just like we hoisted wrenMakeCallHandle() out of our performance critical
@@ -180,13 +186,15 @@ arguments. In our GameEngine example, that’s just the elapsed time. Method
arguments go in consecutive slots after the receiver. So the elapsed time goes
into slot one. You can use any of the slot functions to set this up. For the
example, it’s just:
-wrenSetSlotDouble(vm, 1, elapsedTime);
+wrenSetSlotDouble(vm, 1, elapsedTime);
+
Calling the Method #
We have all of the data in place, so all that’s left is to pull the trigger and
tell the VM to start running some code:
-WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
+WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
+
It takes the method handle we created using wrenMakeCallHandle(). Now Wren
diff --git a/embedding/configuring-the-vm.html b/embedding/configuring-the-vm.html
index 42b3bb7e..3148bfdd 100644
--- a/embedding/configuring-the-vm.html
+++ b/embedding/configuring-the-vm.html
@@ -67,23 +67,25 @@
WrenConfiguration structure. Since Wren has no global state, you can configure
each VM differently if your application happens to run multiple.
The struct looks like:
-typedef struct
-{
- WrenReallocateFn reallocateFn;
- WrenLoadModuleFn loadModuleFn;
- WrenBindForeignMethodFn bindForeignMethodFn;
- WrenBindForeignClassFn bindForeignClassFn;
- WrenWriteFn writeFn;
- WrenErrorFn errorFn;
- size_t initialHeapSize;
- size_t minHeapSize;
- int heapGrowthPercent;
-} WrenConfiguration;
+typedef struct
+{
+ WrenReallocateFn reallocateFn;
+ WrenLoadModuleFn loadModuleFn;
+ WrenBindForeignMethodFn bindForeignMethodFn;
+ WrenBindForeignClassFn bindForeignClassFn;
+ WrenWriteFn writeFn;
+ WrenErrorFn errorFn;
+ size_t initialHeapSize;
+ size_t minHeapSize;
+ int heapGrowthPercent;
+} WrenConfiguration;
+
Most fields have useful defaults, which you can (and should) initialize by
calling:
-wrenInitConfiguration(&configuration);
+wrenInitConfiguration(&configuration);
+
Calling this ensures that your VM doesn’t get uninitialized configuration when
@@ -98,7 +100,8 @@ not know how to talk to the file system, so when an import statemen
executed, it relies on the host application to locate and read the source code
for a module.
The signature of this function is:
-char* loadModule(WrenVM* vm, const char* name)
+char* loadModule(WrenVM* vm, const char* name)
+
When a module is imported, Wren calls this and passes in the module’s name. The
@@ -126,7 +129,8 @@ what you expect.
This is the callback Wren uses to output text when System.print() or the other
related functions are called. This is the minimal connection the VM has with the
outside world and lets you do rudimentary “printf debugging”. Its signature is:
-void write(WrenVM* vm, const char* text)
+void write(WrenVM* vm, const char* text)
+
Wren does not have a default implementation for this. It’s up to you to wire
@@ -135,26 +139,28 @@ calls to System.print() and others silently do nothing.
errorFn #
Wren uses this callback to report compile time and runtime errors. Its signature
is:
-void error(
- WrenVM* vm,
- WrenErrorType type,
- const char* module,
- int line,
- const char* message)
+void error(
+ WrenVM* vm,
+ WrenErrorType type,
+ const char* module,
+ int line,
+ const char* message)
+
The type parameter is one of:
-typedef enum
-{
- // A syntax or resolution error detected at compile time.
- WREN_ERROR_COMPILE,
+typedef enum
+{
+ // A syntax or resolution error detected at compile time.
+ WREN_ERROR_COMPILE,
- // The error message for a runtime error.
- WREN_ERROR_RUNTIME,
+ // The error message for a runtime error.
+ WREN_ERROR_RUNTIME,
- // One entry of a runtime error's stack trace.
- WREN_ERROR_STACK_TRACE
-} WrenErrorType;
+ // One entry of a runtime error's stack trace.
+ WREN_ERROR_STACK_TRACE
+} WrenErrorType;
+
When a compile error occurs, errorFn is called once with type
@@ -171,7 +177,8 @@ or function.
These fields control how the VM allocates and manages memory.
reallocateFn #
This lets you provide a custom memory allocation function. Its signature is:
-void* reallocate(void* memory, size_t newSize)
+void* reallocate(void* memory, size_t newSize)
+
Wren uses this one function to allocate, grow, shrink, and deallocate memory.
diff --git a/embedding/index.html b/embedding/index.html
index 8f2827d4..70d98757 100644
--- a/embedding/index.html
+++ b/embedding/index.html
@@ -135,7 +135,8 @@ write. In return, you get an API that is quite fast.
In either case, you also want to add src/include to your include path so you
can find the public header for Wren:
-#include "wren.h"
+#include "wren.h"
+
Wren depends only on the C standard library, so you don’t usually need to link
@@ -144,21 +145,24 @@ functions in math.h are implemented in a separate library,
If your program is in C++ but you are linking to the Wren library compiled as C,
this header handles the differences in calling conventions between C and C++:
-#include "wren.hpp"
+#include "wren.hpp"
+
Creating a Wren VM #
Once you’ve integrated the code into your executable, you need to create a
virtual machine. To do that, you create a WrenConfiguration:
-WrenConfiguration config;
-wrenInitConfiguration(&config);
+WrenConfiguration config;
+wrenInitConfiguration(&config);
+
This gives you a basic configuration that has reasonable defaults for
everything. If you don’t need to tweak stuff, you can leave it at that. We’ll
learn more about what you can configure later.
With this ready, you can create the VM:
-WrenVM* vm = wrenNewVM(&config);
+WrenVM* vm = wrenNewVM(&config);
+
This allocates memory for a new VM and initializes it. The Wren C implementation
@@ -170,8 +174,9 @@ can discard the WrenConfiguration struct you filled in. Now you have a live
VM, waiting to run some code!
Executing Wren code #
You execute a string of Wren source code like so:
-WrenInterpretResult result = wrenInterpret(vm,
- "System.print(\"I am running in a VM!\")");
+WrenInterpretResult result = wrenInterpret(vm,
+ "System.print(\"I am running in a VM!\")");
+
The string is a series of one or more statements separated by newlines. Wren
@@ -191,7 +196,8 @@ to a REPL session.
Shutting down a VM #
Once the party is over and you’re ready to end your relationship with a VM, you
need to free any memory it allocated. You do that like so:
-wrenFreeVM(vm);
+wrenFreeVM(vm);
+
After calling that, you obviously cannot use the WrenVM* you passed to it
diff --git a/embedding/slots-and-handles.html b/embedding/slots-and-handles.html
index 3bb339f1..0c6bf897 100644
--- a/embedding/slots-and-handles.html
+++ b/embedding/slots-and-handles.html
@@ -78,14 +78,16 @@ notes on for the other side to process.
The array is zero-based, and each slot can hold a value of any type. It is
dynamically sized, but it’s your responsibility to ensure there are enough slots
before you use them. You do this by calling:
-wrenEnsureSlots(WrenVM* vm, int slotCount);
+wrenEnsureSlots(WrenVM* vm, int slotCount);
+
This grows the slot array if needed to ensure that many slots are available. If
it’s already big enough, this does nothing. You’ll typically call this once
before populating the slots with data that you want to send to Wren.
-wrenEnsureSlots(vm, 4);
-// Can now use slots 0 through 3, inclusive.
+wrenEnsureSlots(vm, 4);
+// Can now use slots 0 through 3, inclusive.
+
After you ensure an array of slots, you can only rely on them being there until
@@ -95,7 +97,8 @@ you pass control back to Wren. That includes calling wrenCall() or
no guarantees about what will happen. I’ve heard rumors of smoke and feathers
flying out of a user’s computer.
If you want to see how big the slot array is, use:
-int wrenGetSlotCount(WrenVM* vm);
+int wrenGetSlotCount(WrenVM* vm);
+
It returns the number of slots in the array. Note that this may be higher than
@@ -107,20 +110,22 @@ enough slots for the objects it is sending you.
Once you have some slots, you store data in them using a number of functions all
named wrenSetSlot<type>() where <type> is the kind of data. We’ll start with
the simple ones:
-void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
-void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
-void wrenSetSlotNull(WrenVM* vm, int slot);
+void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
+void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
+void wrenSetSlotNull(WrenVM* vm, int slot);
+
Each of these takes a primitive C value and converts it to the corresponding
Wren value. (Since Wren’s native number type is a double, there’s not
really much conversion going on, but you get the idea.)
You can also pass string data to Wren:
-void wrenSetSlotBytes(WrenVM* vm, int slot,
- const char* bytes, size_t length);
+void wrenSetSlotBytes(WrenVM* vm, int slot,
+ const char* bytes, size_t length);
-void wrenSetSlotString(WrenVM* vm, int slot,
- const char* text);
+void wrenSetSlotString(WrenVM* vm, int slot,
+ const char* text);
+
Both of these copy the bytes into a new String object managed by Wren’s
@@ -132,15 +137,17 @@ regular strings if you happen to know the length. The latter calculates the
length of the string using strlen().
Reading slots #
You can, of course, also pull data out of slots. Here are the simple ones:
-bool wrenGetSlotBool(WrenVM* vm, int slot);
-double wrenGetSlotDouble(WrenVM* vm, int slot);
+bool wrenGetSlotBool(WrenVM* vm, int slot);
+double wrenGetSlotDouble(WrenVM* vm, int slot);
+
These take a Wren value of the corresponding type and convert it to its raw C
representation. For strings, we have:
-const char* wrenGetSlotString(WrenVM* vm, int slot);
-const char* wrenGetSlotBytes(WrenVM* vm, int slot,
- int* length);
+const char* wrenGetSlotString(WrenVM* vm, int slot);
+const char* wrenGetSlotBytes(WrenVM* vm, int slot,
+ int* length);
+
These return a pointer to the first byte of the string. If you want to know the
@@ -154,7 +161,8 @@ correct type. If you read a number from a slot that currently holds a string,
you’re gonna have a bad time.
Fortunately, you usually know what type of data you have in a slot. If not, you
can ask:
-WrenType wrenGetSlotType(WrenVM* vm, int slot);
+WrenType wrenGetSlotType(WrenVM* vm, int slot);
+
This returns an enum defining what type of value is in the slot. It only covers
@@ -165,8 +173,9 @@ simple primitive values first or use a foreign cla
Looking up variables #
There are a few other utility functions that move data into and out of slots.
Here’s the first:
-void wrenGetVariable(WrenVM* vm, const char* module,
- const char* name, int slot);
+void wrenGetVariable(WrenVM* vm, const char* module,
+ const char* name, int slot);
+
This looks up a top level variable with the given name in the module with the
@@ -184,14 +193,16 @@ C, but sometimes you need to shuttle a larger or dynamically-sized ball of
stuff. List objects work well for that, so the C API lets you work
with them directly.
You can create a new empty list from C using:
-void wrenSetSlotNewList(WrenVM* vm, int slot);
+void wrenSetSlotNewList(WrenVM* vm, int slot);
+
It stores the resulting list in the given slot. If you have a list in a
slot—either one you created from C or from Wren—you can add elements
to it using:
-void wrenInsertInList(WrenVM* vm, int listSlot, int index,
- int elementSlot);
+void wrenInsertInList(WrenVM* vm, int listSlot, int index,
+ int elementSlot);
+
That’s a lot of int parameters:
@@ -235,13 +246,15 @@ have two limitations:
To address those, we have handles. A handle wraps a reference to an object of
any kind—strings, numbers, instances of classes, collections, whatever.
You create a handle using this:
-WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
+WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
+
This takes the object stored in the given slot, creates a new WrenHandle to wrap
it, and returns a pointer to it back to you. You can send that wrapped object
back to Wren by calling:
-void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
+void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
+
Note that this doesn’t invalidate your WrenHandle. You can still keep using it.
@@ -256,7 +269,8 @@ That way, during garbage collection, it can find them all and make sure their
objects aren’t freed. But what if you don’t want it to be kept around any more?
Since C relies on manual memory management, WrenHandle does too. When you are
done with one, you must explicitly release it by calling:
-void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
+void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
+
This does not immediately delete the wrapped object—after all, there may
diff --git a/embedding/storing-c-data.html b/embedding/storing-c-data.html
index 6afa07da..c6969152 100644
--- a/embedding/storing-c-data.html
+++ b/embedding/storing-c-data.html
@@ -75,9 +75,10 @@ checks on, etc. But it also wraps a blob of raw memory that is opaque to Wren
but accessible from C.
Defining a Foreign Class #
You define one like so:
-foreign class Point {
+foreign class Point {
// ...
-}
+}
+
The foreign keyword tells Wren to loop in the host application when it
@@ -89,26 +90,29 @@ an instance of the foreign class. This function is found through a binding
process similar to how foreign methods are bound. When you configure
the VM, you set the bindForeignClassFn field in WrenConfiguration to point
to a C callback you define. Its signature must be:
-WrenForeignClassMethods bindForeignClass(
- WrenVM* vm, const char* module, const char* className);
+WrenForeignClassMethods bindForeignClass(
+ WrenVM* vm, const char* module, const char* className);
+
Wren invokes this callback once when a foreign class declaration is executed.
Wren passes in the name of the module containing the foreign class, and the name
of the class being declared. The host’s responsibility is to return one of these
structs:
-typedef struct
-{
- WrenForeignMethodFn allocate;
- WrenFinalizerFn finalize;
-} WrenForeignClassMethods;
+typedef struct
+{
+ WrenForeignMethodFn allocate;
+ WrenFinalizerFn finalize;
+} WrenForeignClassMethods;
+
It’s a pair of function pointers. The first, allocate, is called by Wren
whenever an instance of the foreign class is created. (We’ll get to the optional
finalize callback later.) The allocation callback has the same signature as a
foreign method:
-void allocate(WrenVM* vm);
+void allocate(WrenVM* vm);
+
Initializing an Instance #
@@ -116,8 +120,9 @@ foreign method:
constructors, Wren invokes the allocate callback you gave it when binding
the foreign class. Your primary responsibility in that callback is to tell Wren
how many bytes of raw memory you need. You do that by calling:
-void* wrenSetSlotNewForeign(WrenVM* vm,
- int slot, int classSlot, size_t size);
+void* wrenSetSlotNewForeign(WrenVM* vm,
+ int slot, int classSlot, size_t size);
+
Like other slot manipulation functions, it both reads from and writes to
@@ -143,7 +148,8 @@ can also be used in other foreign methods:
So, for example, if you wanted to create a foreign instance that contains eight
bytes of C data, you’d call:
-void* data = wrenSetSlotNewForeign(vm, 0, 0, 8);
+void* data = wrenSetSlotNewForeign(vm, 0, 0, 8);
+
The value returned by wrenSetSlotNewForeign() is the raw pointer to the
@@ -162,7 +168,8 @@ class is through other foreign methods. Those are usually defined on the same
foreign class, but can be defined on other classes as well. Wren doesn’t care.
Once you have a foreign instance in a slot, you can access the raw bytes it
stores by calling:
-void* wrenGetSlotForeign(WrenVM* vm, int slot);
+void* wrenGetSlotForeign(WrenVM* vm, int slot);
+
You pass in the slot index containing the foreign object and it gives you back a
@@ -196,7 +203,8 @@ have unfettered access to the VM. It’s not like a normal foreign method wh
you can monkey around with slots and other stuff. Doing that while the GC is
running could leave Wren in a weird state.
Instead, the finalize callback’s signature is only:
-void finalize(void* data);
+void finalize(void* data);
+
Wren gives you the pointer to your foreign function’s memory, and that’s it. The
@@ -207,12 +215,13 @@ referenced by that memory.
with a finalizer and a couple of methods. We’ll do a File class that wraps the
C standard file API.
In Wren, the class we want looks like this:
-foreign class File {
+foreign class File {
construct create(path) {}
foreign write(text)
foreign close()
-}
+}
+
So you can create a new file given a path. Once you have one, you can write to
@@ -220,59 +229,62 @@ it and then explicitly close it if you want. We also need to make sure the file
gets closed if the user forgets to and the GC cleans up the object.
Setting up the VM #
Over in the host, first we’ll set up the VM:
-#include "wren.h"
+#include "wren.h"
-int main(int argc, const char* argv[])
-{
- WrenConfiguration config;
- wrenInitConfiguration(&config);
+int main(int argc, const char* argv[])
+{
+ WrenConfiguration config;
+ wrenInitConfiguration(&config);
- config.bindForeignClassFn = bindForeignClass;
- config.bindForeignMethodFn = bindForeignMethod;
+ config.bindForeignClassFn = bindForeignClass;
+ config.bindForeignMethodFn = bindForeignMethod;
- WrenVM* vm = wrenNewVM(&config);
- wrenInterpret(vm, "some code...");
+ WrenVM* vm = wrenNewVM(&config);
+ wrenInterpret(vm, "some code...");
- return 0;
-}
+ return 0;
+}
+
Binding the foreign class #
We give the VM two callbacks. The first is for wiring up the foreign class
itself:
-WrenForeignClassMethods bindForeignClass(
- WrenVM* vm, const char* module, const char* className)
-{
- WrenForeignClassMethods methods;
+WrenForeignClassMethods bindForeignClass(
+ WrenVM* vm, const char* module, const char* className)
+{
+ WrenForeignClassMethods methods;
- if (strcmp(className, "File") == 0)
- {
- methods->allocate = fileAllocate;
- methods->finalize = fileFinalize;
- }
- else
- {
- // Unknown class.
- methods->allocate = NULL;
- methods->finalize = NULL;
- }
+ if (strcmp(className, "File") == 0)
+ {
+ methods->allocate = fileAllocate;
+ methods->finalize = fileFinalize;
+ }
+ else
+ {
+ // Unknown class.
+ methods->allocate = NULL;
+ methods->finalize = NULL;
+ }
- return methods;
-}
+ return methods;
+}
+
When our binding callback is invoked for the File class, we return the allocate
and finalize functions the VM should call. Allocation looks like:
-#include <stdio.h>
-#include "wren.h"
+#include <stdio.h>
+#include "wren.h"
-void fileAllocate(WrenVM* vm)
-{
- FILE** file = (FILE**)wrenSetSlotNewForeign(vm,
- 0, 0, sizeof(FILE*));
- const char* path = wrenGetSlotString(vm, 1);
- *file = fopen(path, "w");
-}
+void fileAllocate(WrenVM* vm)
+{
+ FILE** file = (FILE**)wrenSetSlotNewForeign(vm,
+ 0, 0, sizeof(FILE*));
+ const char* path = wrenGetSlotString(vm, 1);
+ *file = fopen(path, "w");
+}
+
First we create the instance by calling wrenSetSlotNewForeign(). We tell it to
@@ -286,21 +298,23 @@ store that back into the foreign instance using *file. Now we have
object that wraps an open file handle.
The finalizer simply casts the foreign instance’s data back to the proper type
and closes the file:
-void fileFinalize(void* data)
-{
- closeFile((FILE**) data);
-}
+void fileFinalize(void* data)
+{
+ closeFile((FILE**) data);
+}
+
It uses this little utility function:
-static void closeFile(FILE** file)
-{
- // Already closed.
- if (*file == NULL) return;
+static void closeFile(FILE** file)
+{
+ // Already closed.
+ if (*file == NULL) return;
- fclose(*file);
- *file = NULL;
-}
+ fclose(*file);
+ *file = NULL;
+}
+
This closes the file (if it’s not already closed) and also nulls out the file
@@ -309,45 +323,47 @@ handle so that we don’t try to use the file after it’s been closed.
That’s the foreign class part. Now we have a couple of foreign methods to
handle. The host tells the VM how to find them by giving Wren a pointer to this
function:
-WrenForeignMethodFn bindForeignMethod(WrenVM* vm, const char* module,
- const char* className, bool isStatic, const char* signature)
-{
- if (strcmp(className, "File") == 0)
- {
- if (!isStatic && strcmp(signature, "write(_)") == 0)
- {
- return fileWrite;
- }
+WrenForeignMethodFn bindForeignMethod(WrenVM* vm, const char* module,
+ const char* className, bool isStatic, const char* signature)
+{
+ if (strcmp(className, "File") == 0)
+ {
+ if (!isStatic && strcmp(signature, "write(_)") == 0)
+ {
+ return fileWrite;
+ }
- if (!isStatic && strcmp(signature, "close()") == 0)
- {
- return fileClose;
- }
- }
+ if (!isStatic && strcmp(signature, "close()") == 0)
+ {
+ return fileClose;
+ }
+ }
- // Unknown method.
- return NULL;
-}
+ // Unknown method.
+ return NULL;
+}
+
When Wren calls this, we look at the class and method name to figure out which
method it’s binding, and then return a pointer to the appropriate function. The
foreign method for writing to the file is:
-void fileWrite(WrenVM* vm)
-{
- FILE** file = (FILE**)wrenGetSlotForeign(vm, 0);
+void fileWrite(WrenVM* vm)
+{
+ FILE** file = (FILE**)wrenGetSlotForeign(vm, 0);
- // Make sure the file is still open.
- if (*file == NULL)
- {
- wrenSetSlotString(vm, 0, "Cannot write to a closed file.");
- wrenAbortFiber(vm, 0);
- return;
- }
+ // Make sure the file is still open.
+ if (*file == NULL)
+ {
+ wrenSetSlotString(vm, 0, "Cannot write to a closed file.");
+ wrenAbortFiber(vm, 0);
+ return;
+ }
- const char* text = wrenGetSlotString(vm, 1);
- fwrite(text, sizeof(char), strlen(text), *file);
-}
+ const char* text = wrenGetSlotString(vm, 1);
+ fwrite(text, sizeof(char), strlen(text), *file);
+}
+
We use wrenGetSlotForeign() to pull the foreign data out of the slot array.
@@ -358,19 +374,21 @@ pointer.
We do a little sanity checking to make sure the user isn’t writing to a file
they already closed. If not, we call fwrite() to write to the file.
The other method is close() to let them explicitly close the file:
-void fileClose(WrenVM* vm)
-{
- FILE** file = (FILE**)wrenGetSlotForeign(vm, 0);
- closeFile(file);
-}
+void fileClose(WrenVM* vm)
+{
+ FILE** file = (FILE**)wrenGetSlotForeign(vm, 0);
+ closeFile(file);
+}
+
It uses the same helper we defined above. And that’s it, a complete foreign
class with a finalizer and a couple of foreign methods. In Wren, you can use it
like so:
-var file = File.create("some/path.txt")
+var file = File.create("some/path.txt")
file.write("some text")
-file.close()
+file.close()
+
Pretty neat, right? The resulting class looks and feels like a normal Wren
diff --git a/error-handling.html b/error-handling.html
index c70e7b25..615ea255 100644
--- a/error-handling.html
+++ b/error-handling.html
@@ -99,23 +99,27 @@
Syntax errors #
The first errors you’re likely to run into are syntax errors. These include
simple bugs where your code doesn’t follow the language’s grammar, like:
-1 + * 2
+1 + * 2
+
Wren detects these errors as soon as it tries to read your code. When it hits
one, you get a friendly error message, like:
-[main line 1] Error on '*': Unexpected token for expression.
+[main line 1] Error on '*': Unexpected token for expression.
+
Some slightly more “semantic” errors fall into this bucket too. Things like
using a variable that hasn’t been defined, or declaring two variables with the
same name in the same scope. So if you do:
-var a = "once"
-var a = "twice"
+var a = "once"
+var a = "twice"
+
Wren tells you:
-[main line 2] Error on 'a': Top-level variable is already defined.
+[main line 2] Error on 'a': Top-level variable is already defined.
+
Note that it does this before it executes any code. Unlike some other
@@ -132,16 +136,18 @@ statically. Since they can’t be found until your code is run, they’r
perform an operation that the VM can’t do. The most common error is a “method
not found” one. If you call a method on an object and its class (and all of its
superclasses) don’t define that method, there’s nothing Wren can do:
-class Foo {
- construct new() {}
-}
+class Foo {
+ construct new() {}
+}
-var foo = Foo.new()
-foo.someRandomMethod
+var foo = Foo.new()
+foo.someRandomMethod
+
If you run this, Wren will print:
-Foo does not implement method 'someRandomMethod'.
+Foo does not implement method 'someRandomMethod'.
+
Then it stops executing code. Unlike some other languages, Wren doesn’t keep
@@ -152,13 +158,15 @@ and all of the method calls that led to it.
Another common runtime error is passing an argument of the wrong type to a
method. For example, lists are indexed using a number. If you try to pass some
other type, it’s an error:
-var list = ["a", "b", "c"]
-list["1"]
+var list = ["a", "b", "c"]
+list["1"]
+
This exits with:
-Subscript must be a number or a range.
-[main line 2] in (script)
+Subscript must be a number or a range.
+[main line 2] in (script)
+
These are the two most common kinds of runtime errors, but there are others.
@@ -176,26 +184,30 @@ invoked that one, all the way to the main fiber, and then exit the VM.
in the called fiber, the error is captured and the try method returns the
error message as a string.
For example, if you run this program:
-var fiber = Fiber.new {
+var fiber = Fiber.new {
123.badMethod
}
var error = fiber.try()
-System.print("Caught error: " + error)
+System.print("Caught error: " + error)
+
It prints:
-Caught error: Num does not implement method 'badMethod'.
+Caught error: Num does not implement method 'badMethod'.
+
The called fiber can no longer be used, but any other fibers can proceed as
usual. When a fiber has been aborted because of a runtime error, you can also
get the error from the fiber object. Continuing the above example:
-System.print(fiber.error)
+System.print(fiber.error)
+
This also prints:
-Num does not implement method 'badMethod'.
+Num does not implement method 'badMethod'.
+
If you have a chain of fiber calls and a runtime error occurs, it will walk the
@@ -205,7 +217,8 @@ errors generated in fibers that are invoked by the one you called tryMost runtime errors come from within the Wren VM, but you may want to be able
to cause your own runtime errors to occur. This can be done by calling the
abort() static method on Fiber:
-Fiber.abort("Something bad happened")
+Fiber.abort("Something bad happened")
+
You must pass in an error message, and it must be a string.
diff --git a/functions.html b/functions.html
index 5bffb740..fc92b54e 100644
--- a/functions.html
+++ b/functions.html
@@ -109,9 +109,10 @@ you’re filtering on.
Since that’s the most common usage pattern, Wren’s syntax optimizes for that.
Taking a page from Ruby, a function is created by passing a block argument to
a method. At its simplest, it looks like this:
-blondie.callMe {
+blondie.callMe {
System.print("This is the body!")
-}
+}
+
Here we’re invoking the callMe method on blondie. We’re passing one
@@ -120,36 +121,40 @@ following block—everything between that p
curly braces.
Methods that take a block argument receive it as a normal parameter. callMe
could be defined like so:
-class Blondie {
- callMe(fn) {
- // Call it...
- }
-}
+class Blondie {
+ callMe(fn) {
+ // Call it...
+ }
+}
-var blondie = Blondie.new()
+var blondie = Blondie.new()
+
A method can take other arguments in addition to the block. They appear before
the block just like a regular argument list. For example:
-blondie.callMeAt(867, 5309) {
+blondie.callMeAt(867, 5309) {
System.print("This is the body!")
-}
+}
+
Of course, you don’t have to use a block argument to pass a function to a
method. If you already have a function object, you can pass it like a regular
argument:
-var someFn = // Get a function...
-blondie.callMe(someFn)
+var someFn = // Get a function...
+blondie.callMe(someFn)
+
Block arguments are purely sugar for creating a function and passing it in one
little blob of syntax. There are some times when you want to create a function
but don’t need to pass it to a method. For that, you can call the Fn
class’s constructor:
-var someFn = Fn.new {
+var someFn = Fn.new {
System.print("Hi!")
-}
+}
+
As you can see it takes a block argument too! All the constructor does it
@@ -157,23 +162,25 @@ return that, so this exists purely as a convenience method for you.
Calling functions #
Once you have a function, how do you invoke it? Like everything in Wren, you do
so by calling a method on it:
-class Blondie {
- callMe(fn) {
- fn.call()
- }
-}
+class Blondie {
+ callMe(fn) {
+ fn.call()
+ }
+}
+
Functions expose a call() method that executes the body of the function. This
method is dynamically-dispatched like any other, so you can define your own
“function-like” classes and pass them to methods that expect “real” functions.
-class FakeFn {
- call() {
- System.print("I'm feeling functional!")
- }
-}
+class FakeFn {
+ call() {
+ System.print("I'm feeling functional!")
+ }
+}
-blondie.callMe(FakeFn.new())
+blondie.callMe(FakeFn.new())
+
Function parameters #
@@ -181,18 +188,20 @@ blondie.callMe(FakeFn.new())
functions that we’ve seen so far take no arguments. To change that, you can
provide a parameter list surrounded by | immediately after the opening brace
of the body, like so:
-blondie.callMe {|first, last|
+blondie.callMe {|first, last|
System.print("Hi, " + first + " " + last + "!")
-}
+}
+
Here we’re passing a function to greet that takes two parameters, first and
last. They are passed to the function when it’s called:
-class Blondie {
- callMe(fn) {
- fn.call("Debbie", "Harry")
- }
-}
+class Blondie {
+ callMe(fn) {
+ fn.call("Debbie", "Harry")
+ }
+}
+
It’s an error to call a function with fewer arguments than its parameter list
@@ -205,33 +214,36 @@ expression.
Otherwise, the body returns null by default. You can explicitly return a
value using a return statement. In other words, these two functions do the
same thing:
-Fn.new { "return value" }
+Fn.new { "return value" }
Fn.new {
return "return value"
-}
+}
+
Closures #
As you expect, functions are closures—they can access variables defined
outside of their scope. They will hold onto closed-over variables even after
leaving the scope where the function is defined:
-class Counter {
- static create() {
- var i = 0
- return Fn.new { i = i + 1 }
- }
-}
+class Counter {
+ static create() {
+ var i = 0
+ return Fn.new { i = i + 1 }
+ }
+}
+
Here, the create method returns the function created on its second line. That
function references a variable i declared outside of the function. Even after
the function is returned from create, it is still able to read and assign
toi:
-var counter = Counter.create()
+var counter = Counter.create()
System.print(counter.call()) //> 1
System.print(counter.call()) //> 2
-System.print(counter.call()) //> 3
+System.print(counter.call()) //> 3
+
Classes →
diff --git a/getting-started.html b/getting-started.html
index 9b6ea269..2d67a410 100644
--- a/getting-started.html
+++ b/getting-started.html
@@ -114,10 +114,11 @@ few dependencies are nice that way. “Wren” encompasses two separate
If you’re on a Unix or Mac and you can rock a command line, it’s just:
-$ git clone https://github.com/munificent/wren.git
-$ cd wren
+$ git clone https://github.com/munificent/wren.git
+$ cd wren
$ make
-$ ./wren
+$ ./wren
+
This builds both the VM and the CLI. The release build of the CLI goes right
@@ -128,7 +129,8 @@ Windows brethren, util/msvc2013 contains a Visual Studio solution.
that these may not have the exact same build settings as the makefile. The
makefile is the “official” way to compile Wren.
If you only want to build the VM, you can do:
-$ make vm
+$ make vm
+
This compiles the VM to static and shared libraries.
@@ -136,11 +138,13 @@ makefile is the “official” way to compile Wren.
If you just run wren without any arguments, it starts the interpreter in
interactive mode. You can type in a line of code, and it immediately executes
it. Here’s something to try:
-System.print("Hello, world!")
+System.print("Hello, world!")
+
Or a little more exciting:
-for (i in 1..10) System.print("Counting up %(i)")
+for (i in 1..10) System.print("Counting up %(i)")
+
You can exit the interpreter using good old Ctrl-C or Ctrl-D, or just throw
@@ -149,7 +153,7 @@ your computer to the ground and storm off.
The standalone interpreter can also load scripts from files and run them. Just
pass the name of the script to wren. Create a file named “my_script.wren” in
your favorite text editor and paste this into it:
-for (yPixel in 0...24) {
+for (yPixel in 0...24) {
var y = yPixel / 12 - 1
for (xPixel in 0...80) {
var x = xPixel / 30 - 2
@@ -167,11 +171,13 @@ your favorite text editor and paste this into it:
}
System.print("")
-}
+}
+
Now run:
-$ ./wren my_script.wren
+$ ./wren my_script.wren
+
Neat, right? You’re a Wren programmer now! The next step is to learn the
diff --git a/index.html b/index.html
index 9cbf67de..b779823e 100644
--- a/index.html
+++ b/index.html
@@ -98,19 +98,20 @@
Wren is a small, fast, class-based concurrent scripting language #
Think Smalltalk in a Lua-sized package with a dash of Erlang and wrapped up in
a familiar, modern syntax.
-System.print("Hello, world!")
+System.print("Hello, world!")
-class Wren {
- flyTo(city) {
- System.print("Flying to %(city)")
- }
-}
+class Wren {
+ flyTo(city) {
+ System.print("Flying to %(city)")
+ }
+}
-var adjectives = Fiber.new {
- ["small", "clean", "fast"].each {|word| Fiber.yield(word) }
-}
+var adjectives = Fiber.new {
+ ["small", "clean", "fast"].each {|word| Fiber.yield(word) }
+}
-while (!adjectives.isDone) System.print(adjectives.call())
+while (!adjectives.isDone) System.print(adjectives.call())
+
diff --git a/lists.html b/lists.html
index 9aba9a06..24cd6f52 100644
--- a/lists.html
+++ b/lists.html
@@ -98,7 +98,8 @@
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
expressions inside square brackets:
-
[1, "banana", true]
+[1, "banana", true]
+
Here, we’ve created a list of three elements. Notice that the elements don’t
@@ -107,25 +108,29 @@ have to be the same type.
You can access an element from a list by calling the subscript
operator on it with the index of the
element you want. Like most languages, indexes start at zero:
-var hirsute = ["sideburns", "porkchops", "'stache", "goatee"]
+var hirsute = ["sideburns", "porkchops", "'stache", "goatee"]
System.print(hirsute[0]) //> sideburns
-System.print(hirsute[1]) //> porkchops
+System.print(hirsute[1]) //> porkchops
+
Negative indices counts backwards from the end:
-System.print(hirsute[-1]) //> goatee
-System.print(hirsute[-2]) //> 'stache
+System.print(hirsute[-1]) //> goatee
+System.print(hirsute[-2]) //> 'stache
+
It’s a runtime error to pass an index outside of the bounds of the list. If you
don’t know what those bounds are, you can find out using count:
-System.print(hirsute.count) //> 4
+System.print(hirsute.count) //> 4
+
Slices and ranges #
Sometimes you want to copy a chunk of elements from a list. You can do that by
passing a range to the subscript operator, like so:
-System.print(hirsute[1..2]) //> [porkchops, 'stache]
+System.print(hirsute[1..2]) //> [porkchops, 'stache]
+
This returns a new list containing the elements of the original list whose
@@ -133,24 +138,28 @@ indices are within the given range. Both inclusive and exclusive ranges work
and do what you expect.
Negative bounds also work like they do when passing a single number, so to copy
a list, you can just do:
-hirsute[0..-1]
+hirsute[0..-1]
+
Adding elements #
Lists are mutable, meaning their contents can be changed. You can swap out an
existing element in the list using the subscript setter:
-hirsute[1] = "muttonchops"
-System.print(hirsute[1]) //> muttonchops
+hirsute[1] = "muttonchops"
+System.print(hirsute[1]) //> muttonchops
+
It’s an error to set an element that’s out of bounds. To grow a list, you can
use add to append a single item to the end:
-hirsute.add("goatee")
-System.print(hirsute.count) //> 4
+hirsute.add("goatee")
+System.print(hirsute.count) //> 4
+
You can insert a new element at a specific position using insert:
-hirsute.insert(2, "soul patch")
+hirsute.insert(2, "soul patch")
+
The first argument is the index to insert at, and the second is the value to
@@ -159,29 +168,33 @@ make room for it.
It’s valid to “insert” after the last element in the list, but only right
after it. Like other methods, you can use a negative index to count from the
back. Doing so counts back from the size of the list after it’s grown by one:
-var letters = ["a", "b", "c"]
+var letters = ["a", "b", "c"]
letters.insert(3, "d") // OK: inserts at end.
System.print(letters) //> [a, b, c, d]
letters.insert(-2, "e") // Counts back from size after insert.
-System.print(letters) //> [a, b, c, e, d]
+System.print(letters) //> [a, b, c, e, d]
+
Removing elements #
The opposite of insert is removeAt. It removes a single element from a
given position in the list. All following items are shifted up to fill in the
gap:
-var letters = ["a", "b", "c", "d"]
+var letters = ["a", "b", "c", "d"]
letters.removeAt(1)
-System.print(letters) //> [a, c, d]
+System.print(letters) //> [a, c, d]
+
The removeAt method returns the removed item:
-System.print(letters.removeAt(1)) //> c
+System.print(letters.removeAt(1)) //> c
+
If you want to remove everything from the list, you can clear it:
-hirsute.clear()
-System.print(hirsute) //> []
+hirsute.clear()
+System.print(hirsute) //> []
+
Maps →
diff --git a/maps.html b/maps.html
index d9dfc6e7..15b002e0 100644
--- a/maps.html
+++ b/maps.html
@@ -100,12 +100,13 @@ maps a key to a value. The same data structure has a variety o
other languages: hash table, dictionary, association, table, etc.
You can create a map by placing a series of comma-separated entries inside
curly braces. Each entry is a key and a value separated by a colon:
-{
+{
"George": "Harrison",
"John": "Lennon",
"Paul": "McCartney",
"Ringo": "Starr"
-}
+}
+
This creates a map that associates the first name of each Beatle with his last
@@ -123,10 +124,11 @@ time, even in very large maps. Since Wren only knows how to hash certain
built-in types, only those can be used as keys.
Adding entries #
You add new key-value pairs to the map using the subscript operator:
-var capitals = {}
+var capitals = {}
capitals["Georgia"] = "Atlanta"
capitals["Idaho"] = "Boise"
-capitals["Maine"] = "Augusta"
+capitals["Maine"] = "Augusta"
+
If the key isn’t already present, this adds it and associates it with the given
@@ -134,41 +136,47 @@ value. If the key is already there, this just replaces its value.
Looking up values #
To find the value associated with some key, again you use your friend the
subscript operator:
-System.print(capitals["Idaho"]) //> Boise
+System.print(capitals["Idaho"]) //> Boise
+
If the key is present, this returns its value. Otherwise, it returns null. Of
course, null itself can also be used as a value, so seeing null here
doesn’t necessarily mean the key wasn’t found.
To tell definitively if a key exists, you can call containsKey():
-var belief = {"nihilism": null}
+var belief = {"nihilism": null}
System.print(belief["nihilism"]) //> null (though key exists)
System.print(belief["solipsism"]) //> null
System.print(belief.containsKey("nihilism")) //> true
-System.print(belief.containsKey("solipsism")) //> false
+System.print(belief.containsKey("solipsism")) //> false
+
You can see how many entries a map contains using count:
-System.print(capitals.count) //> 3
+System.print(capitals.count) //> 3
+
Removing entries #
To remove an entry from a map, call remove() and pass in the key for the
entry you want to delete:
-capitals.remove("Maine")
-System.print(capitals.containsKey("Maine")) //> false
+capitals.remove("Maine")
+System.print(capitals.containsKey("Maine")) //> false
+
If the key was found, this returns the value that was associated with it:
-System.print(capitals.remove("Georgia")) //> Atlanta
+System.print(capitals.remove("Georgia")) //> Atlanta
+
If the key wasn’t in the map to begin with, remove() just returns null.
If you want to remove everything from the map, like with lists, you call
clear():
-capitals.clear()
-System.print(capitals.count) //> 0
+capitals.clear()
+System.print(capitals.count) //> 0
+
Iterating over the contents #
@@ -179,7 +187,7 @@ For that, map exposes two methods: keys and values.
map, and the second returns one that iterates over the values.
If you want to see all of the key-value pairs in a map, the easiest way is to
iterate over the keys and use each to look up its value:
-var birds = {
+var birds = {
"Arizona": "Cactus wren",
"Hawaii": "Nēnē",
"Ohio": "Northern Cardinal"
@@ -187,7 +195,8 @@ iterate over the keys and use each to look up its value:
for (state in birds.keys) {
System.print("The state bird of " + state + " is " + birds[state])
-}
+}
+
This program prints the three states and their birds. However, the order
diff --git a/method-calls.html b/method-calls.html
index 9f62a918..41123f3e 100644
--- a/method-calls.html
+++ b/method-calls.html
@@ -97,17 +97,20 @@
Method Calls
Wren is deeply object oriented, so most code consists of invoking methods on
objects, usually something like this:
-System.print("Heyoo!") //> Heyoo!
+System.print("Heyoo!") //> Heyoo!
+
You have a receiver expression (here System) followed by a ., then a name
(print) and an argument list in parentheses (("Heyoo!")). Multiple arguments
are separated by commas:
-list.insert(3, "item")
+list.insert(3, "item")
+
The argument list can also be empty:
-list.clear()
+list.clear()
+
The VM executes a method call like so:
@@ -124,9 +127,10 @@ takes. In technical terms, this means you can overload by arity.
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:
-var random = Random.new()
+var random = Random.new()
random.int(3, 10)
-random.int(4)
+random.int(4)
+
In a language like Python or JavaScript, these would both call a single int()
@@ -144,18 +148,20 @@ any “if I got two arguments do this…” runtime work.
Getters #
Some methods exist to expose a stored or computed property of an object. These
are getters and have no parentheses:
-"string".count //> 6
+"string".count //> 6
(1..10).min //> 1
1.23.sin //> 0.9424888019317
-[1, 2, 3].isEmpty //> false
+[1, 2, 3].isEmpty //> false
+
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:
-"string".count()
-[1, 2, 3].clear
+"string".count()
+[1, 2, 3].clear
+
If you’re defining some member that doesn’t need any parameters, you need to
@@ -164,15 +170,17 @@ The general guidelines are:
-
If it modifies the object or has some other side effect, make it a method:
-list.clear()
+list.clear()
+
-
If the method supports multiple arities, make the zero-parameter case a ()
method to be consistent with the other versions:
-Fiber.yield()
-Fiber.yield("value")
+Fiber.yield()
+Fiber.yield("value")
+
@@ -183,7 +191,8 @@ Fiber.yield("value")
Setters #
A getter lets an object expose a public “property” that you can read.
Likewise, a setter lets you write to a property:
-person.height = 74 // Grew up!
+person.height = 74 // Grew up!
+
Despite the =, this is just another syntax for a method call. From the
@@ -195,19 +204,21 @@ provide a read/write property.
Operators #
Wren has most of the same operators you know and love with the same precedence
and associativity. We have three prefix operators:
-! ~ -
+! ~ -
+
They are just method calls on their operand without any other arguments. An
-expression like !possible means “call the ! method on possible“.
+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:
-* / % + - .. ... << >> < <= > >= == != & ^ | is
+* / % + - .. ... << >> < <= > >= == != & ^ | is
+
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“.
+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
@@ -220,20 +231,23 @@ like mocks or proxies where you want an object to masquerade as a certain class.
Subscripts #
Another familiar syntax from math class is subscripting using square brackets
([]). It’s handy for working with collection-like objects. For example:
-list[0] // Get the first item in a list.
-map["key"] // Get the value associated with "key".
+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:
-matrix[3, 5]
+matrix[3, 5]
+
These examples are subscript “getters”, and there are also
corresponding subscript setters:
-list[0] = "item"
-map["key"] = "value"
+list[0] = "item"
+map["key"] = "value"
+
These are equivalent to method calls whose signature is [_]=(_) and whose
diff --git a/modularity.html b/modularity.html
index 41937bbd..268ec498 100644
--- a/modularity.html
+++ b/modularity.html
@@ -119,7 +119,8 @@ a name collision. Each module is, well, modular.
When you run Wren and give it a file name to execute, the contents of that file
define the “main” module that execution starts at. To load and execute other
modules, you use an import statement:
-import "beverages" for Coffee, Tea
+import "beverages" for Coffee, Tea
+
This finds a module named “beverages” and executes its source code. Then, it
@@ -127,14 +128,16 @@ looks up two top-level variables, Coffee and Tea in this module with their values.
This statement can appear anywhere a variable declaration is allowed, even
inside blocks:
-if (thirsty) {
- import "beverages" for Coffee, Tea
-}
+if (thirsty) {
+ import "beverages" for Coffee, Tea
+}
+
If you want to load a module, but not bind any variables from it, you can omit
the for clause:
-import "some_imperative_code"
+import "some_imperative_code"
+
That’s the basic idea. Now let’s break it down into each of the steps it
@@ -153,16 +156,18 @@ to uniquely identify the module. The embedding application controls how that
string is used to locate a blob of source code.
When the host application creates a new Wren VM, it provides a module loader
function:
-WrenConfiguration config;
-config.loadModuleFn = loadModule;
+WrenConfiguration config;
+config.loadModuleFn = loadModule;
-// Other configuration...
+// Other configuration...
-WrenVM* vm = wrenNewVM(&config);
+WrenVM* vm = wrenNewVM(&config);
+
That function has this signature:
-char* WrenLoadModuleFn(WrenVM* vm, const char* name);
+char* WrenLoadModuleFn(WrenVM* vm, const char* name);
+
Whenever a module is imported, the VM calls this and passes it the name of the
@@ -176,11 +181,13 @@ found. When you do this, Wren will report it as a runtime error.
The default little command-line VM that comes with Wren has a very simple
lookup process. It appends the module name and “.wren” to the directory where
the main module was loaded and looks for that file. So, let’s say you run:
-$ wren /code/my_program.wren
+$ wren /code/my_program.wren
+
And that main module has:
-import "some/module"
+import "some/module"
+
Then the command-line VM will try to find /code/some/module.wren. By
@@ -209,7 +216,8 @@ These are simply variables declared outside of any
method or function.
These are visible to anything inside the module, but they can also be
exported and used by other modules. When Wren executes an import like:
-import "beverages" for Coffee, Tea
+import "beverages" for Coffee, Tea
+
First it runs the “beverages” module. Then it goes through each of the variable
@@ -228,18 +236,19 @@ rarely makes a difference.
Earlier, I described a program’s set of modules as a tree. Of course, it’s only
a tree of modules if there are no shared imports. But consider a program
like:
-// main.wren
-import "a"
-import "b"
+// main.wren
+import "a"
+import "b"
-// a.wren
-import "shared"
+// a.wren
+import "shared"
-// b.wren
-import "shared"
+// b.wren
+import "shared"
-// shared.wren
-System.print("Shared!")
+// shared.wren
+System.print("Shared!")
+
Here, “a” and “b” both want to use “shared”. If “shared” defines some top-level
@@ -267,38 +276,41 @@ the registry before it is executed. This means if an import for that sa
module is reached while the module itself or one of its imports is executing,
it will be found in the registry and the cycle is short-circuited.
For example:
-// main.wren
-import "a"
+// main.wren
+import "a"
-// a.wren
-System.print("start a")
-import "b"
-System.print("end a")
+// a.wren
+System.print("start a")
+import "b"
+System.print("end a")
-// b.wren
-System.print("start b")
-import "a"
-System.print("end b")
+// b.wren
+System.print("start b")
+import "a"
+System.print("end b")
+
This program runs successfully and prints:
-start a
+start a
start b
end b
-end a
+end a
+
Where you have to be careful is binding variables. Consider:
-// main.wren
-import "a"
+// main.wren
+import "a"
-// a.wren
-import "b" for B
-var A = "a variable"
+// a.wren
+import "b" for B
+var A = "a variable"
-// b.wren
-import "a" for A
-var B = "b variable"
+// b.wren
+import "a" for A
+var B = "b variable"
+
The import of “a” in b.wren will fail here. If you trace the execution, you
@@ -313,16 +325,17 @@ get:
defined yet since “a.wren” is still sitting on the import "b" for B line
before the declaration. To get this to work, you would need to move the
variable declaration above the import:
-// main.wren
-import "a"
+// main.wren
+import "a"
-// a.wren
-var A = "a variable"
-import "b" for B
+// a.wren
+var A = "a variable"
+import "b" for B
-// b.wren
-import "a" for A
-var B = "b variable"
+// b.wren
+import "a" for A
+var B = "b variable"
+
Now when we run it, we get:
diff --git a/modules/core/bool.html b/modules/core/bool.html
index 7748f357..25155ce3 100644
--- a/modules/core/bool.html
+++ b/modules/core/bool.html
@@ -83,8 +83,9 @@
Methods #
! operator #
Returns the logical complement of the value.
-System.print(!true) //> false
-System.print(!false) //> true
+System.print(!true) //> false
+System.print(!false) //> true
+
toString #
diff --git a/modules/core/class.html b/modules/core/class.html
index 7b5bec36..6c907aca 100644
--- a/modules/core/class.html
+++ b/modules/core/class.html
@@ -85,18 +85,21 @@
The name of the class.
supertype #
The superclass of this class.
-class Crustacean {}
-class Crab is Crustacean {}
+class Crustacean {}
+class Crab is Crustacean {}
-System.print(Crab.supertype) //> Crustacean
+System.print(Crab.supertype) Crustacean
+
A class with no explicit superclass implicitly inherits Object:
-System.print(Crustacean.supertype) //> Object
+System.print(Crustacean.supertype) //> Object
+
Object forms the root of the class hierarchy and has no supertype:
-System.print(Object.supertype) //> null
+System.print(Object.supertype) //> null
+