From befe90501b41478a0cfdb0239cd018855fd866ad Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Mon, 16 May 2016 08:09:14 -0700 Subject: [PATCH] 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... --- doc/site/embedding-api.markdown | 131 --------- .../embedding/application-lifecycle.markdown | 9 + .../embedding/calling-c-from-wren.markdown | 10 + .../embedding/calling-wren-from-c.markdown | 10 + .../embedding/configuring-the-vm.markdown | 10 + doc/site/embedding/index.markdown | 209 ++++++++++++++ doc/site/embedding/slots-and-values.markdown | 269 ++++++++++++++++++ doc/site/embedding/storing-c-data.markdown | 10 + doc/site/embedding/template.html | 84 ++++++ doc/site/index.markdown | 2 +- doc/site/modules/core/fiber.markdown | 18 +- doc/site/style.scss | 5 +- doc/site/template.html | 4 +- 13 files changed, 629 insertions(+), 142 deletions(-) delete mode 100644 doc/site/embedding-api.markdown create mode 100644 doc/site/embedding/application-lifecycle.markdown create mode 100644 doc/site/embedding/calling-c-from-wren.markdown create mode 100644 doc/site/embedding/calling-wren-from-c.markdown create mode 100644 doc/site/embedding/configuring-the-vm.markdown create mode 100644 doc/site/embedding/index.markdown create mode 100644 doc/site/embedding/slots-and-values.markdown create mode 100644 doc/site/embedding/storing-c-data.markdown create mode 100644 doc/site/embedding/template.html diff --git a/doc/site/embedding-api.markdown b/doc/site/embedding-api.markdown deleted file mode 100644 index 99e0a321..00000000 --- a/doc/site/embedding-api.markdown +++ /dev/null @@ -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. diff --git a/doc/site/embedding/application-lifecycle.markdown b/doc/site/embedding/application-lifecycle.markdown new file mode 100644 index 00000000..bb713ccc --- /dev/null +++ b/doc/site/embedding/application-lifecycle.markdown @@ -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 + +← Configuring the VM diff --git a/doc/site/embedding/calling-c-from-wren.markdown b/doc/site/embedding/calling-c-from-wren.markdown new file mode 100644 index 00000000..5cd580a1 --- /dev/null +++ b/doc/site/embedding/calling-c-from-wren.markdown @@ -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 + +Storing C Data → +← Calling Wren from C diff --git a/doc/site/embedding/calling-wren-from-c.markdown b/doc/site/embedding/calling-wren-from-c.markdown new file mode 100644 index 00000000..aa9b3b9c --- /dev/null +++ b/doc/site/embedding/calling-wren-from-c.markdown @@ -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 + +Calling C from Wren → +← Slots and Values diff --git a/doc/site/embedding/configuring-the-vm.markdown b/doc/site/embedding/configuring-the-vm.markdown new file mode 100644 index 00000000..ff51c622 --- /dev/null +++ b/doc/site/embedding/configuring-the-vm.markdown @@ -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 + +Application Lifecycle → +← Storing C Data diff --git a/doc/site/embedding/index.markdown b/doc/site/embedding/index.markdown new file mode 100644 index 00000000..126c7827 --- /dev/null +++ b/doc/site/embedding/index.markdown @@ -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—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—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... + +Slots and Values → + + diff --git a/doc/site/embedding/slots-and-values.markdown b/doc/site/embedding/slots-and-values.markdown new file mode 100644 index 00000000..ce25e8f7 --- /dev/null +++ b/doc/site/embedding/slots-and-values.markdown @@ -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()` where `` 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—either one you created from C or from Wren—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—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—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... + +Calling Wren from C → +← Introduction diff --git a/doc/site/embedding/storing-c-data.markdown b/doc/site/embedding/storing-c-data.markdown new file mode 100644 index 00000000..9da28009 --- /dev/null +++ b/doc/site/embedding/storing-c-data.markdown @@ -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 + +Configuring the VM → +← Calling C from Wren diff --git a/doc/site/embedding/template.html b/doc/site/embedding/template.html new file mode 100644 index 00000000..352ba122 --- /dev/null +++ b/doc/site/embedding/template.html @@ -0,0 +1,84 @@ + + + + +{title} – Wren + + + + + + +
+
+
+

wren

+

a classy little scripting language

+
+
+
+ + + + diff --git a/doc/site/index.markdown b/doc/site/index.markdown index c0f9853e..24539766 100644 --- a/doc/site/index.markdown +++ b/doc/site/index.markdown @@ -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 diff --git a/doc/site/modules/core/fiber.markdown b/doc/site/modules/core/fiber.markdown index 99583d48..da2e66fc 100644 --- a/doc/site/modules/core/fiber.markdown +++ b/doc/site/modules/core/fiber.markdown @@ -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 diff --git a/doc/site/style.scss b/doc/site/style.scss index c33cc6b6..e15074a5 100644 --- a/doc/site/style.scss +++ b/doc/site/style.scss @@ -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%); } diff --git a/doc/site/template.html b/doc/site/template.html index 89efafa2..0c338f6b 100644 --- a/doc/site/template.html +++ b/doc/site/template.html @@ -45,7 +45,7 @@

reference

@@ -85,7 +85,7 @@