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.

Defining a class #

Classes are created using the class keyword, unsurprisingly:

-
class Unicorn {}
+
class Unicorn {}
+

This creates a class named Unicorn with no methods or fields.

Methods #

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()) +