1
0
forked from Mirror/wren

Start writing comprensive embedding docs.

- Break embedding out into a separate section with multiple pages.
- Document the constraints on the API.
- Document the slot array and WrenValue.

Still lots more to do, but getting there...
This commit is contained in:
Bob Nystrom
2016-05-16 08:09:14 -07:00
parent 856390c3fa
commit befe90501b
13 changed files with 629 additions and 142 deletions

View File

@ -1,131 +0,0 @@
^title Embedding API
Wren is designed to be a scripting language, so the embedding API is as
important as any of its language features. There are two (well, three) ways to
get Wren into your application:
1. **Link to static or dynamic library.** When you [build Wren][build], it
generates both shared and static libraries in `lib` that you can link to.
2. **Include the source directly in your application.** If you want to include
the source directly in your program, you don't need to run any build steps.
Just add the source files in `src/vm` to your project. They should compile
cleanly as C99 or C++98 or anything later.
[build]: getting-started.html
In either case, you also want to add `src/include` to your include path so you
can get to the [public header for Wren][wren.h]:
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
:::c
#include "wren.h"
## Creating a Wren VM
Once you've integrated the code into your executable, you need to create a
virtual machine. To do that, you first fill in a `WrenConfiguration`:
:::c
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. If you
do want to turn some knobs and dials, it exposes some fields you can set:
:::c
config.reallocateFn = ...;
The `reallocateFn` is a callback you can provide to control how Wren allocates
and frees memory. If you leave that to the default, it uses `malloc()` and
`free()`.
:::c
config.loadModuleFn = ...;
config.bindForeignMethodFn = ...;
config.bindForeignClassFn = ...;
These three callbacks are how Wren talks back to your program. We'll cover
them in detail later.
:::c
config.initialHeapSize = ...;
config.minHeapSize = ...;
config.heapGrowthPercent = ...;
These let you tune how the garbage collector runs. You can tweak these if you
want, but the defaults are usually fine.
With this ready, you can create the VM:
:::c
WrenVM* vm = wrenNewVM(&config);
This allocates memory for a new VM using the same `reallocateFn` you provided.
The Wren C implementation has no global state, so every single bit of data Wren
uses is bundled up inside a `WrenVM`. You can have multiple Wren VMs running
independently from each other without any problems.
`wrenNewVM()` stores its own copy of the configuration, so after calling it, you
can discard the `WrenConfiguration` struct you filled in. Now you have a live
VM, waiting to run some code!
## Executing Wren code
You can tell the VM to execute a string of Wren source code like so:
:::c
WrenInterpretResult result = wrenInterpret(vm, "System.print(\"Hi!\")");
The first string is the chunk of code to execute—a series of one or more
statements separated by newlines. Wren runs this code in a special "main"
module. Each time you call this, the code is run in the same module. This way,
top-level names defined in one call can be accessed in later ones.
When you call `wrenInterpret()`, Wren first compiles your source to bytecode. If
an error occurs here, it returns immediately with `WREN_RESULT_COMPILE_ERROR`.
Otherwise, Wren spins up a new [fiber][] and executes the code in that. Your
code can in turn spawn whatever other fibers it wants. It keeps running fibers
until they all complete.
[fiber]: concurrency.html
If a [runtime error][] occurs (and another fiber doesn't catch it), it will
abort fibers all the way back to the main one and then return
`WREN_RESULT_RUNTIME_ERROR`. Otherwise, when the last fiber successfully
returns, it returns `WREN_RESULT_SUCCESS`.
[runtime error]: error-handling.html
## Calling a C function from Wren
**TODO**
## Calling a Wren method from C
**TODO**
## Storing a reference to a Wren object in C
**TODO**
## Storing C data in a Wren object
**TODO**
## 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:
:::c
wrenFreeVM(vm);
After calling that, you obviously cannot use the `WrenVM*` you passed to it
again. It's dead.
Note that Wren will yell at you if you still have any live `WrenValue` objects
when you call this. This makes sure you haven't lost track of any of them (which
leaks memory) and you don't try to use any of them after the VM has been freed.

View File

@ -0,0 +1,9 @@
^title Application Lifecycle
**TODO: Write these docs.**
Until these are written, you can read the docs in [wren.h][].
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
<a href="configuring-the-vm.html">&larr; Configuring the VM</a>

View File

@ -0,0 +1,10 @@
^title Calling C from Wren
**TODO: Write these docs.**
Until these are written, you can read the docs in [wren.h][].
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
<a class="right" href="storing-c-data.html">Storing C Data &rarr;</a>
<a href="calling-wren-from-c.html">&larr; Calling Wren from C</a>

View File

@ -0,0 +1,10 @@
^title Calling Wren from C
**TODO: Write these docs.**
Until these are written, you can read the docs in [wren.h][].
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
<a class="right" href="calling-c-from-wren.html">Calling C from Wren &rarr;</a>
<a href="slots-and-values.html">&larr; Slots and Values</a>

View File

@ -0,0 +1,10 @@
^title Configuring the VM
**TODO: Write these docs.**
Until these are written, you can read the docs in [wren.h][].
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
<a class="right" href="application-lifecycle.html">Application Lifecycle &rarr;</a>
<a href="storing-c-data.html">&larr; Storing C Data</a>

View File

@ -0,0 +1,209 @@
^title Embedding
Wren is designed to be a scripting language that lives inside a host
application, so the embedding API is as important as any of its language
features. Designing this API well requires satisfying several constraints:
1. **Wren is dynamically typed, but C is not.** A variable can hold a value of
any type in Wren, but that's definitely not the case in C unless you define
some sort of variant type, which ultimately just kicks the problem down the
road. Eventually, we have to move data across the boundary between typed and
untyped code.
2. **Wren uses garbage collection, but C manages memory manually.** GC adds a
few constraints on the API. The VM must be able to find every Wren object
that is still usable, even if that object is being held onto by native C
code. Otherwise, it could free an object that's still in use.
Also, we ideally don't want to let native C code see a bare pointer to a
chunk of memory managed by Wren. Many garbage collection strategies involve
[moving objects][] in memory. If we allow C code to point directly to an
object, that pointer will be left dangling when the object moves. To prevent
that, we can't using moving GCs even though they have some nice performance
properties.
3. **The embedding API needs to be fast.** Users may add layers of abstraction
on top of the API to make it more pleasant to work with, but the base API
defines the *maximum* performance you can get out of the system. It's the
bottom of the stack, so there's no way for a user to optimize around it if
it's too slow. There is no lower level alternative.
4. **We want the API to be pleasant to use.** This is the last constraint
because it's the softest. Of course, we want a beautiful, usable, API. But we
really *need* to handle the above, so we're willing to make things a bit more
of a chore to reach the first three goals.
[moving objects]: https://en.wikipedia.org/wiki/Tracing_garbage_collection#Copying_vs._mark-and-sweep_vs._mark-and-don.27t-sweep
Fortunately, Wren isn't the first language to tackle this. If you're familiar
with [Lua's C API][lua], you'll find Wren's to be fairly familiar.
[lua]: https://www.lua.org/pil/24.html
### Performance and safety
When code is safely snuggled within the confines of the VM, it's pretty safe.
Method calls are dynamically checked and generate a runtime error which can be
caught and handled. The stack grows if it gets close to overflowing. In general,
when you're within Wren code, it tries very hard to avoid crashing and burning.
This is why you use a high level language after all&mdash;it's safer and more
productive than C. C, meanwhile, really assumes you know what you're doing. You
can cast pointers in invalid ways, misinterpret bits, use memory after freeing
it, etc. What that gives you in return is blazing performance. Many of the
reasons C is fast are because it takes all the governors and guardrails off.
Wren's embedding API defines the border between those worlds, and takes on some
of the characteristics of C. When you call any of the embedding API functions,
it assumes you are calling them correctly. If you invoke a Wren method from C
that expects three arguments, it trusts that you gave it three arguments.
In debug builds, Wren has assertions to check as many things as it can, but in
release builds, Wren expects you to do the right thing. This means you need to
take care when using the embedding API, just like you do in all C code you
write. In return, you get an API that is quite fast.
## Including Wren
There are two (well, three) ways to get the Wren VM into your program:
1. **Link to the static or dynamic library.** When you [build Wren][build], it
generates both shared and static libraries in `lib` that you can link to.
2. **Include the source directly in your application.** If you want to include
the source directly in your program, you don't need to run any build steps.
Just add the source files in `src/vm` to your project. They should compile
cleanly as C99 or C++98 or anything later.
[build]: ../getting-started.html
In either case, you also want to add `src/include` to your include path so you
can get to the [public header for Wren][wren.h]:
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
:::c
#include "wren.h"
If your program is in C++ but you are linking to the Wren library compiled as C,
then you'll need to handle the calling convention differences like so:
:::c
extern "C" {
#include "wren.h"
}
(Wren's source can be compiled as either C or C++, so you can avoid this by
compiling the whole thing yourself as C++. Whatever floats your boat.)
## 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:
:::c
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][configuration] about what you can configure later.
[configuration]: configuration.html
With this ready, you can create the VM:
:::c
WrenVM* vm = wrenNewVM(&config);
This allocates memory for a new VM and initializes it. The Wren C implementation
has no global state, so every single bit of data Wren uses is bundled up inside
a WrenVM. You can have multiple Wren VMs running independently of each other
without any problems.
`wrenNewVM()` stores its own copy of the configuration, so after calling it, you
can discard the `WrenConfiguration` struct you filled in. Now you have a live
VM, waiting to run some code!
## Executing Wren code
You can tell the VM to execute a string of Wren source code like so:
:::c
WrenInterpretResult result = wrenInterpret(vm,
"System.print(\"I am running in a VM!\")");
The string is a chunk of Wren code to execute&mdash;a series of one or more
statements separated by newlines. Wren copies the string, so you can free it
after calling this.
When you call `wrenInterpret()`, Wren first compiles your source to bytecode. If
an error occurs, it returns immediately with `WREN_RESULT_COMPILE_ERROR`.
Otherwise, Wren spins up a new [fiber][] and executes the code in that. Your
code can in turn spawn whatever other fibers it wants. It keeps running fibers
until they all complete or the current one [suspends].
[fiber]: ../concurrency.html
[suspends]: ../modules/core/fiber.html#fiber.suspend()
The code runs in a special "main" module. Everytime you call `wrenInterpret()`,
it runs in the same module. That way, top-level names defined in one call can be
accessed in later ones.
If a [runtime error][] occurs (and another fiber doesn't handle it), Wren abort
fibers all the way back to the main one and returns `WREN_RESULT_RUNTIME_ERROR`.
Otherwise, when the last fiber successfully returns, it returns
`WREN_RESULT_SUCCESS`.
[runtime error]: error-handling.html
## 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:
:::c
wrenFreeVM(vm);
After calling that, you obviously cannot use the `WrenVM*` you passed to it
again. It's dead.
Note that Wren will yell at you if you still have any live [WrenValue][value]
objects when you call this. This makes sure you haven't lost track of any of
them (which leaks memory) and you don't try to use any of them after the VM has
been freed.
[value]: slots-and-values.html#values
Next, we'll learn to make that VM do useful stuff...
<a class="right" href="slots-and-values.html">Slots and Values &rarr;</a>
<!--
- configuration
- each field and what it means
- preprocessor option
- WREN_NAN_TAGGING
- WREN_COMPUTED_GOTO
- WREN_OPT_META
- WREN_OPT_RANDOM
- WREN_DEBUG_GC_STRESS 0
- WREN_DEBUG_TRACE_MEMORY 0
- WREN_DEBUG_TRACE_GC 0
- WREN_DEBUG_DUMP_COMPILED_CODE 0
- WREN_DEBUG_TRACE_INSTRUCTIONS 0
- calling wren from c
Often a host application wants to load a bunch of Wren code into the VM and
then periodically call a method. For example, a game engine might load all
of the entity scripts into Wren. Then, each tick of the game loop, it calls
Wren to tell all of the entities to update.
- calling c from wren
- storing c data in wren objects
- fibers and scheduling
- app lifecycle, wren in charge
- app lifecycle, c in charge
-->

View File

@ -0,0 +1,269 @@
^title Slots and Values
With `wrenInterpret()`, we can execute code, but that code can't do anything
particularly interesting. Out of the box, the VM is very isolated from the rest
of the world, so pretty much all it can do is burn CPU cycles. It can turn your
laptop into a lap warmer, but that's about it.
To make our Wren code *useful*, the VM needs to communicate with the outside
world. Wren uses a single unified set of functions for passing data into and out
of the VM. These functions are based on two fundamental concepts: **slots** and
**values**.
## The Slot Array
When you want to send data to Wren, read data from it, or generally monkey
around with Wren objects from C, you do so by going through an array of slots.
You can think of it as a shared message board that both the VM and your C code
can leave bits of data on for the other side to process.
The array is zero-based, and each slot holds a value of any type. It is
dynamically sized, but it's your responsibility to ensure there are enough
slots before you start using them. You do this by calling:
:::c
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.
:::c
wrenEnsureSlots(vm, 4);
// Can now use slots 0 through 3, inclusive.
When Wren is [calling your C code][] and passing data to you, it ensures there
are enough slots for the objects it is sending you.
[calling your c code]: calling-c-from-wren.html
After you ensure an array of slots, you can only rely on them being there until
you pass control back to Wren. That includes calling `wrenCall()` or
`wrenInterpret()`, or returning from a [foreign method][].
[foreign method]: calling-c-from-wren.html
If you read or write from a slot that you haven't ensured is valid, Wren makes
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, you can call:
:::c
int wrenGetSlotCount(WrenVM* vm);
It returns the number of slots in the array. Note that this may be higher than
the size you've ensured. Wren reuses the memory for this array when possible,
so you may get one bigger than you need if it happened to be laying around.
### Writing slots
Once you have some slots, you can 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:
:::c
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.)
[wren value]: ../values.html
[native number type]: ../values.html#numbers
You can also pass string data to Wren:
:::c
void wrenSetSlotBytes(WrenVM* vm, int slot,
const char* bytes, size_t length);
void wrenSetSlotString(WrenVM* vm, int slot,
const char* text);
Both of these copy the bytes into a new [String][] object managed by Wren's
garbage collector, so you can free your copy of it after you call this. The
difference between the two is that `wrenSetSlotBytes()` takes an explicit
length. Since Wren supports strings containing arbitrary byte values, including
the null byte, this lets you pass those in. It's also a little faster to use
this for regular strings if you happen to know the length. The latter calculates
the length of the string using `strlen()`.
[string]: ../values.html#strings
### Reading slots
You can, of course, also pull data out of slots. Here are the simple ones:
:::c
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:
:::c
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
length, the latter stores it in the variable pointed to by `length`. Both of
these return a direct pointer to the bytes managed by Wren. You should not hold
on to this pointer for long. Wren does not promise that it won't move or free
the data.
With these functions, you are going from dynamically typed Wren data to
statically typed C. It's up to *you* to ensure that you read a value as the
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:
:::c
WrenType wrenGetSlotType(WrenVM* vm, int slot);
This returns an enum defining what type of value is in the slot. It only covers
the primitive values that are supported by the C API. Thinks like ranges and
instances of classes come back as `WREN_TYPE_UNKNOWN`. If you want to move that
kind of data between Wren and C, you'll have to pull the object apart into
simple primitive values first or use a [foreign class][].
[foreign class]: storing-c-data.html
### Looking up variables
There are a few other utility functions that move data into and out of slots.
Here's the first:
:::c
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
given name and stores its value in the given slot. (Code you executed using
`wrenInterpret()` implicitly goes into a module named "main".)
Note that classes are just objects stored in variables too, so you can use this
to look up a class by its name. Handy for calling static methods on it.
Like any method that works with strings, this one is a bit slow. It has to
hash the name and look it up in the module's string table. You might want to
avoid calling this in the middle of a hot loop where performance is critical.
Instead, it's faster to look up the variable once outside the loop and store a
reference to the object using a [WrenValue](#values).
### Working with lists
The slot array is fine for moving a fixed number of objects between Wren and
C, but sometimes you need to shuttle a larger or dynamically-sized ball of
stuff. [List objects][lists] work well for that, so the C API lets you work
with them directly.
[lists]: ../lists.html
You can create a new empty list from C using:
:::c
void wrenSetSlotNewList(WrenVM* vm, int slot);
It stores the resulting list in the given slot. If you have a list in a
slot&mdash;either one you created from C or from Wren&mdash;you can add elements
to it using:
:::c
void wrenInsertInList(WrenVM* vm, int listSlot, int index,
int elementSlot);
That's a lot of int parameters:
* `listSlot` is the slot where the list object is stored. That's the list you'll
be modifying. If you created the list from C, it will be the slot you passed
to `wrenSetSlotNewList()`.
* `index` is the index within the list where you want to insert the element.
Just like from within Wren, you can use a negative number to count back from
the end, so `-1` appends to the list.
* `elementSlot` identifies the slot where the value you want to insert in the
list can be found.
This API means getting a value from C into a list is a two step operation. First
you move the value into a slot, then you take it from the slot and insert it in
the list. This is kind of tedious, but it lets us use the same set of functions
for moving values into slots of each primitive type. Otherwise, we'd need
`wrenInsertInListDouble()`, `wrenInsertInListBool()`, etc.
## Values
Slots are pretty good for shuttling primitive data between C and Wren, but they
have two limitations:
1. **They are short-lived.** As soon as you execute some more Wren code, the
slot array is invalidated. You can't use a slot to persistently keep track
of some object.
2. **They only support primitive types.** A slot can hold a value of any type,
but the C API we've seen so far doesn't let you *do* anything with values
that aren't simple primitive ones. If you want to grab a reference to,
say, an instance of some class, how do you do it?
To address those, we have WrenValue. A WrenValue is a handle that wraps a
reference to an object of any kind&mdash;strings, numbers, instances of classes,
collections, whatever.
*(Note: WrenValue will probably be renamed to WrenHandle soon.)*
You create a WrenValue using this:
:::c
WrenValue* wrenGetSlotValue(WrenVM* vm, int slot);
This takes the object stored in the given slot, creates a new WrenValue to wrap
it, and returns a pointer to it back to you.
You can send that wrapped object back to Wren by calling:
:::c
void wrenSetSlotValue(WrenVM* vm, int slot, WrenValue* value);
Note that this doesn't invalidate your WrenValue. You can still keep using it.
### Retaining and releasing values
A WrenValue is an opaque wrapper around an object of any type, but just as
important, it's a *persistent* one. When Wren gives you a pointer to a
WrenValue, it guarantees that that pointer remains valid. You can keep it
around as long as you want. Even if a garbage collection occurs, Wren will
ensure all of the WrenValues and the objects they wrap are kept safely in
memory.
Internally, Wren keeps a list of all of the WrenValues that have been created.
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, WrenValue does too. When you are done with one, you
must explicitly release it by calling:
:::c
void wrenReleaseValue(WrenVM* vm, WrenValue* value);
This does not immediately delete the wrapped object&mdash;after all, there may
be other references to the same object in the program. It just invalidates the
WrenValue wrapper itself. After you call this, you cannot use that pointer
again.
You must release every WrenValue you've created before shutting down the VM.
Wren warns you if you don't, since it implies you've probably leaked a resource
somewhere.
Now we know how to pass values between Wren and C, but we don't know how to
actually *do* anything with them. Next, we'll learn how to use slots to pass
parameters to a Wren method from C...
<a class="right" href="calling-wren-from-c.html">Calling Wren from C &rarr;</a>
<a href="index.html">&larr; Introduction</a>

View File

@ -0,0 +1,10 @@
^title Storing C Data
**TODO: Write these docs.**
Until these are written, you can read the docs in [wren.h][].
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
<a class="right" href="configuring-the-vm.html">Configuring the VM &rarr;</a>
<a href="calling-c-from-wren.html">&larr; Calling C from Wren</a>

View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<link rel="stylesheet" type="text/css" href="../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
the viewport. -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
</head>
<body id="top" class="embedding">
<header>
<div class="page">
<div class="main-column">
<h1><a href="../">wren</a></h1>
<h2>a classy little scripting language</h2>
</div>
</div>
</header>
<div class="page">
<nav class="big">
<section>
<h2>embedding</h2>
<ul>
<li><a href="./">Introduction</a></li>
<li><a href="slots-and-values.html">Slots and Values</a></li>
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
<li><a href="storing-c-data.html">Storing C Data</a></li>
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
<li><a href="application-lifecycle.html">Application Lifecycle</a></li>
</ul>
</section>
</nav>
<nav class="small">
<table>
<tr>
<td><h2>embedding</h2></td>
<td><h2>?</h2></td>
<td><h2>?</h2></td>
</tr>
<tr>
<td>
<ul>
<li><a href="./">Introduction</a></li>
<li><a href="slots-and-values.html">Slots and Values</a></li>
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
<li><a href="storing-c-data.html">Storing C Data</a></li>
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
<li><a href="application-lifecycle.html">Application Lifecycle</a></li>
</ul>
</td>
<td>
<ul>
</ul>
</td>
<td>
<ul>
</ul>
</td>
</tr>
</table>
</nav>
<main>
<h1>{title}</h1>
{html}
</main>
</div>
<footer>
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/munificent/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>
</footer>
</body>
</html>

View File

@ -51,7 +51,7 @@ involved][contribute]!
[perf]: performance.html
[classes]: classes.html
[fibers]: concurrency.html
[embedding]: embedding-api.html
[embedding]: embedding
[started]: getting-started.html
[browser]: http://ppvk.github.io/wren-nest/
[contribute]: contributing.html

View File

@ -25,13 +25,17 @@ The currently executing fiber.
Pauses the current fiber, and stops the interpreter. Control returns to the
host application.
To resume execution, the host application will need to invoke the interpreter
again. If there is still a reference to the suspended fiber, it can be resumed.
Typically, you store a reference to the fiber using `Fiber.current` before
calling this. The fiber can be resumed later by calling or transferring to that
reference. If there are no references to it, it is eventually garbage collected.
Much like `yield()`, returns the value passed to `call()` or `transfer()` when
the fiber is resumed.
### Fiber.**yield**()
Pauses the current fiber and transfers control to the parent fiber. "Parent"
here means the last fiber that was started using `call` and not `run`.
here means the last fiber that was started using `call` and not `transfer`.
:::wren
var fiber = Fiber.new {
@ -46,8 +50,8 @@ here means the last fiber that was started using `call` and not `run`.
When resumed, the parent fiber's `call()` method returns `null`.
If a yielded fiber is resumed by calling `call()` or `run()` with an argument,
`yield()` returns that value.
If a yielded fiber is resumed by calling `call()` or `transfer()` with an
argument, `yield()` returns that value.
:::wren
var fiber = Fiber.new {
@ -57,8 +61,8 @@ If a yielded fiber is resumed by calling `call()` or `run()` with an argument,
fiber.call() // Run until the first yield.
fiber.call("value") // Resume the fiber.
If it was resumed by calling `call()` or `run()` with no argument, it returns
`null`.
If it was resumed by calling `call()` or `transfer()` with no argument, it
returns `null`.
If there is no parent fiber to return to, this exits the interpreter. This can
be useful to pause execution until the host application wants to resume it

View File

@ -277,13 +277,16 @@ footer {
span.c1, span.cm { color: mix($code-color, $code-bg, 60%); }
// Keywords.
span.k, span.kd, span.kc, span.nb { color: hsl(200, 70%, 50%); }
span.k, span.kd, span.kc, span.kt, span.nb { color: hsl(200, 70%, 50%); }
// Names.
span.vg { color: hsl(180, 70%, 35%); }
span.vi { color: hsl(90, 80%, 35%); }
span.vc { color: hsl(130, 60%, 40%); }
// C function name.
span.nf { color: hsl(180, 60%, 40%); }
// Numbers.
span.m, span.mi, span.mf { color: hsl(90, 40%, 50%); }

View File

@ -45,7 +45,7 @@
<h2>reference</h2>
<ul>
<li><a href="modules">Modules</a></li>
<li><a href="embedding-api.html">Embedding API</a></li>
<li><a href="embedding">Embedding</a></li>
<li><a href="performance.html">Performance</a></li>
<li><a href="qa.html">Q &amp; A</a></li>
</ul>
@ -85,7 +85,7 @@
<td>
<ul>
<li><a href="modules">Modules</a></li>
<li><a href="embedding-api.html">Embedding API</a></li>
<li><a href="embedding">Embedding</a></li>
<li><a href="performance.html">Performance</a></li>
<li><a href="qa.html">Q &amp; A</a></li>
</ul>