5 Commits

Author SHA1 Message Date
64c799e39a additional documentation updates 2020-12-03 09:26:10 -08:00
8d0074634f update documentation for WrenLoadModuleResult 2020-12-03 09:22:06 -08:00
559ee1a4ca remove unused length param 2020-12-03 08:57:07 -08:00
1a95253824 Add userData pointer (as suggested by @dethraid) 2020-07-11 12:19:59 -07:00
6d3739af65 Introduce WrenLoadModuleResult, fix unfreed strings from host.
The original attempt at handling the returns from loadModuleFn wasn't ideal. 889cae5ff1

Instead of making the host go via the VM allocation and need to understand it semantically, we can instead solve the problem of the unfreed return result directly.

This also opens up the option of providing a length parameter or other information needed later (length is optional, and not used as of right now, but exists to show intent).
2020-07-11 11:59:24 -07:00
138 changed files with 511 additions and 3555 deletions

View File

@ -25,8 +25,4 @@ Michal Kozakiewicz <michalkozakiewicz3@gmail.com>
Charlotte Koch <cfkoch@edgebsd.org>
Michel Hermier <michel.hermier@gmail.com>
Taylor Hoff <primdevs@gmail.com>
ruby0x1 <ruby0x1@pm.me>
Kolja Kube <code@koljaku.be>
Alexander Klingenbeck <alexander.klingenbeck@gmx.de>
Aviv Beeri <avbeeri@gmail.com>
ruby0x1 <ruby0x1@pm.me>

View File

@ -1,54 +1,3 @@
## 0.4.0
### Language
- Add `continue` keyword
- Add `as`: `import "..." for Name as OtherName`
- Add Support positive sign in scientific notation
- Add Fiber.try(value) to complement Fiber.call(value)
- Allow `.` to be on a different line (for fluent/builder APIs)
### Modules
- Random: Random.sample optimizations
- List:
- add `list.sort()` and `list.sort {|a, b| ... }` (quicksort)
- add `list.swap(index0, index1)` for swapping elements within a list
- add `list.indexOf(value)` for finding values in a list
- Num:
- add `Num.tau`
- add `Num.nan`
- add `Num.infinity`
- add `min(other)`
- add `max(other)`
- add `clamp(min, max)`
- add `exp`
- add `log2`
### Fixes
- Fix stack corruption related to `Fn` calls
- Fix a byte offset bug in CODE_IMPORT_VARIABLE
- Fix some stack corruptions related to multiple wrenInterpret calls
- Fixed crash when GC collects module during import
- Fix `Bool`, `Num` and `Null` allowing subclassing, which is invalid
### API
- BREAKING: Add `userData` to `wrenReallocateFn`
- BREAKING: Add `WrenLoadModuleResult` which has a `onComplete` callback, allowing freeing module strings
- Add `wrenHasVariable` and `wrenHasModule` queries, for use with `wrenGetVariable`
- Add `wrenSetListElement` to complement `wrenGetListElement`, and allow negative index for both
- Add Map functions to API
- wrenSetSlotNewMap
- wrenGetMapCount
- wrenGetMapContainsKey
- wrenGetMapValue
- wrenSetMapValue
- wrenRemoveMapValue
### Other
- build; add util/generate_docs.py for regenerating project files
- vm; Allow computed goto when using clang on Windows
- vm; WREN_MAX_TEMP_ROOTS default is 8 (instead of 5)
- vm; GC debug times are printed in milliseconds, not seconds
## 0.3.0
0.3.0 is a fairly specific release, aimed at fixing build issues across platforms,

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2013-2021 Robert Nystrom and Wren Contributors
Copyright (c) 2013-2020 Robert Nystrom and Wren Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -44,7 +44,7 @@ If you like the sound of this, [let's get started][started]. You can even try
it [in your browser][browser]! Excited? Well, come on and [get
involved][contribute]!
[![Build Status](https://travis-ci.org/wren-lang/wren.svg?branch=main)](https://travis-ci.org/wren-lang/wren)
[![Build Status](https://travis-ci.org/wren-lang/wren.svg)](https://travis-ci.org/wren-lang/wren)
[syntax]: http://wren.io/syntax.html
[src]: https://github.com/wren-lang/wren/tree/main/src

View File

@ -267,7 +267,7 @@ inside a method works like this:
So, in the above example, we hit case #2 and it prints "Francis". Distinguishing
self sends from outer variables based on the *case* of the first letter in the
name probably seems weird but it works surprisingly well. Method names are
name probably seems crazy but it works surprisingly well. Method names are
lowercase in Wren. Class names are capitalized.
Most of the time, when you're in a method and want to access a name from outside
@ -347,20 +347,6 @@ overloaded by [arity](#signature). A constructor *must* be a named method with
a (possibly empty) argument list. Operators, getters, and setters cannot be
constructors.
A constructor returns the instance of the class being created, even if you
don't explicitly use `return`. It is valid to use `return` inside of a
constructor, but it is an error to have an expression after the return.
That rule applies to `return this` as well, return handles that implicitly inside
a constructor, so just `return` is enough.
<pre class="snippet">
return //> valid, returns 'this'
return variable //> invalid
return null //> invalid
return this //> also invalid
</pre>
A constructor is actually a pair of methods. You get a method on the class:
<pre class="snippet">
@ -635,131 +621,6 @@ class Derived is Base {
}
</pre>
## Attributes
<small>**experimental stage**: subject to minor changes</small>
A class and methods within a class can be tagged with 'meta attributes'.
Like this:
<pre class="snippet">
#hidden = true
class Example {}
</pre>
These attributes are metadata, they give you a way to annotate and store
any additional information about a class, which you can optionally access at runtime.
This information can also be used by external tools, to provide additional
hints and information from code to the tool.
<small>
Since this feature has just been introduced, **take note**.
**Currently** there are no attributes with a built-in meaning.
Attributes are user-defined metadata. This may not remain
true as some may become well defined through convention or potentially
through use by Wren itself.
</small>
Attributes are placed before a class or method definition,
and use the `#` hash/pound symbol.
They can be
- a `#key` on it's own
- a `#key = value`
- a `#group(with, multiple = true, keys = "value")`
An attribute _key_ can only be a `Name`. This is the same type of name
as a method name, a class name or variable name, an identifier that matches
the Wren identifier rules. A name results in a String value at runtime.
An attribute _value_ can be any of these literal values: `Name, String, Bool, Num`.
Values cannot contain expressions, just a value, there is no compile time
evaluation.
Groups can span multiple lines, methods have their own attributes, and duplicate
keys are valid.
<pre class="snippet">
#key
#key = value
#group(
multiple,
lines = true,
lines = 0
)
class Example {
#test(skip = true, iterations = 32)
doStuff() {}
}
</pre>
### Accessing attributes at runtime
By default, attributes are compiled out and ignored.
For an attribute to be visible at runtime, mark it for runtime
access using an exclamation:
<pre class="snippet">
#doc = "not runtime data"
#!runtimeAccess = true
#!maxIterations = 16
</pre>
Attributes at runtime are stored on the class. You can access them via
`YourClass.attributes`. The `attributes` field on a class will
be null if a class has no attributes or if it's attributes aren't marked.
If the class contains class or method attributes, it will be an object with
two getters:
- `YourClass.attributes.self` for the class attributes
- `YourClass.attributes.methods` for the method attributes
Attributes are stored by group in a regular Wren Map.
Keys that are not grouped, use `null` as the group key.
Values are stored in a list, since duplicate keys are allowed, multiple
values need to be stored. They're stored in order of definition.
Method attributes are stored in a map by method signature, and each method
has it's own attributes that match the above structure. The method signature
is prefixed by `static` or `foreign static` as needed.
Let's see what that looks like:
<pre class="snippet">
// Example.attributes.self =
// {
// null: { "key":[null] },
// group: { "key":[value, 32, false] }
// }
#!key
#ignored //compiled out
#!group(key=value, key=32, key=false)
class Example {
#!getter
getter {}
// { regular(_,_): { regular:[null] } }
#!regular
regular(arg0, arg1) {}
// { static other(): { isStatic:[true] } }
#!isStatic = true
static other()
// { foreign static example(): { isForeignStatic:[32] } }
#!isForeignStatic=32
foreign static example()
}
</pre>
<br><hr>
<a class="right" href="concurrency.html">Concurrency &rarr;</a>
<a href="functions.html">&larr; Functions</a>

View File

@ -1,7 +1,5 @@
^title Module "scheduler"
This module provides a vehicle to allow other operations to be performed asynchronously whilst waiting for the main operation to be completed.
It contains a single class:
**TODO**
* [Scheduler](scheduler.html)

View File

@ -1,27 +1,7 @@
^title Scheduler Class
The Scheduler class maintains a list of fibers, to be started one after the other, when a signal to do so is received. The signal (a private method call) is typically transmitted by _long running_ methods in the File or Timer classes which suspend the current fiber so that Wren can carry out other tasks in the meantime.
**TODO**
## Static Method
## Methods
### Scheduler.**add**(callable)
Adds a new fiber to the scheduler's fibers list. This fiber calls `callable` and then transfers to the next fiber in the list, if there is one.
`callable` is a function or other object which has a call() method.
<pre class="snippet">
var a = 3
Scheduler.add {
a = a * a
}
Scheduler.add {
a = a + 1
}
System.print(a) // still 3
Timer.sleep(3000) // wait 3 seconds
System.print(a) // now 3 * 3 + 1 = 10
</pre>
**TODO**

View File

@ -1,7 +1,5 @@
^title Module "timer"
This module provides a mechanism to suspend the current fiber for a given period of time either as a simple delay or to allow other operations to be performed asynchronously in the meantime.
It contains a single class:
**TODO**
* [Timer](timer.html)

View File

@ -1,12 +1,7 @@
^title Timer Class
## Static Method
**TODO**
### Timer.**sleep**(milliseconds)
Suspends the current fiber for the given number of milliseconds. It is a runtime error if this is not a non-negative number.
This method is often used in conjunction with the Scheduler class which runs any scheduled tasks in separate fibers whilst the current fiber is sleeping.
Note that this method also suspends the System.clock method which will not give the correct running time for a program as a result.
## Methods
**TODO**

View File

@ -88,7 +88,8 @@ The basic process is simple:
If there are no failures, you're good to go.
2. **[Fork the repo][fork] so you can change it locally.** Please make your
changes in separate [feature branches][] to make things a little easier.
changes in separate [feature branches][] to make things a little easier on
me.
3. **Change the code.** Please follow the style of the surrounding code. That
basically means `camelCase` names, `{` on the next line, keep within 80
@ -104,7 +105,8 @@ The basic process is simple:
6. **Add your name and email to the [AUTHORS][] file if you haven't already.**
7. **Send a [pull request][].** Pat yourself on the back for contributing to a
fun open source project!
fun open source project! I'll take it from here and hopefully we'll get it
landed smoothly.
## Getting help

View File

@ -65,7 +65,7 @@ if (ready) {
Unlike most other [operators][] in Wren which are just a special syntax for
[method calls][], the `&&` and `||` operators are special. This is because they
only conditionally evaluate the right operand&mdash;they short-circuit.
only conditionally evaluate right operand&mdash;they short-circuit.
[operators]: method-calls.html#operators
[method calls]: method-calls.html
@ -92,7 +92,7 @@ System.print(1 || 2) //> 1
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 similar languages.
and its brethren.
<pre class="snippet">
System.print(1 != 2 ? "math is sane" : "math is not sane!")
@ -177,21 +177,6 @@ for (i in [1, 2, 3, 4]) {
} //> 3
</pre>
## Continue statements
During the execution of a loop body, you might decide that you want to skip the
rest of this iteration and move on to the next one. You can use a `continue`
statement to do that. It's just the `continue` keyword all by itself. Execution
will immediately jump to the beginning of the next loop iteration (and check the
loop conditions).
<pre class="snippet">
for (i in [1, 2, 3, 4]) {
System.print(i) //> 1
if (i == 2) continue //> 3
} //> 4
</pre>
## Numeric ranges
Lists are one common use for `for` loops, but sometimes you want to walk over a

View File

@ -82,7 +82,7 @@ static void loadModuleComplete(WrenVM* vm,
{
if(result.source) {
//for example, if we used malloc to allocate
//our source string, we use free to release it.
our source string, we use free to release it.
free((void*)result.source);
}
}
@ -182,7 +182,7 @@ These fields control how the VM allocates and manages memory.
This lets you provide a custom memory allocation function. Its signature is:
<pre class="snippet" data-lang="c">
void* reallocate(void* memory, size_t newSize, void* userData)
void* reallocate(void* memory, size_t newSize)
</pre>
Wren uses this one function to allocate, grow, shrink, and deallocate memory.

View File

@ -128,8 +128,8 @@ And then, we update the configuration to point to it.
<pre class="snippet" data-lang="c">
WrenConfiguration config;
wrenInitConfiguration(&config);
config.writeFn = &writeFn;
wrenInitConfiguration(&config);
</pre>
[configuration]: configuring-the-vm.html
@ -200,71 +200,6 @@ 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.
## A complete example
Below is a complete example of the above.
You can find this file in the [example](https://github.com/wren-lang/wren/blob/main/example/embedding/main.c) folder.
<pre class="snippet" data-lang="c">
//For more details, visit https://wren.io/embedding/
#include <stdio.h>
#include "wren.h"
static void writeFn(WrenVM* vm, const char* text)
{
printf("%s", text);
}
void errorFn(WrenVM* vm, WrenErrorType errorType,
const char* module, const int line,
const char* msg)
{
switch (errorType)
{
case WREN_ERROR_COMPILE:
{
printf("[%s line %d] [Error] %s\n", module, line, msg);
} break;
case WREN_ERROR_STACK_TRACE:
{
printf("[%s line %d] in %s\n", module, line, msg);
} break;
case WREN_ERROR_RUNTIME:
{
printf("[Runtime Error] %s\n", msg);
} break;
}
}
int main()
{
WrenConfiguration config;
wrenInitConfiguration(&config);
config.writeFn = &writeFn;
config.errorFn = &errorFn;
WrenVM* vm = wrenNewVM(&config);
const char* module = "main";
const char* script = "System.print(\"I am running in a VM!\")";
WrenInterpretResult result = wrenInterpret(vm, module, script);
switch (result) {
case WREN_RESULT_COMPILE_ERROR:
{ printf("Compile Error!\n"); } break;
case WREN_RESULT_RUNTIME_ERROR:
{ printf("Runtime Error!\n"); } break;
case WREN_RESULT_SUCCESS:
{ printf("Success!\n"); } break;
}
wrenFreeVM(vm);
}
</pre>
[handle]: slots-and-handles.html#handles
Next, we'll learn to make that VM do useful stuff...

View File

@ -1,43 +1,129 @@
^title Functions
Like many languages today, functions in Wren are little bundles of code
you can store in a variable, or pass as an argument to a method.
Notice there's a difference between _function_ and _method_.
Since Wren is object-oriented, most of your code will live in methods on
classes, but free-floating functions are still eminently handy.
No self-respecting language today can get by without functions&mdash;first
class little bundles of code. Since Wren is object-oriented, most of your code
will live in methods on classes, but free-floating functions are still
eminently handy.
Functions are objects like everything else in Wren, instances of the `Fn`
class.
## Creating a function
## Block arguments
To create a function, we call `Fn.new`, which takes a block to execute.
To call the function, we use `.call()` on the function instance.
Most of the time you create a function just to pass it to some method. For
example, if you want to filter a [list](lists.html) by some criteria, you'll
call its `where` method, passing in a function that defines the predicate
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:
<pre class="snippet">
var sayHello = Fn.new { System.print("hello") }
sayHello.call() //> hello
blondie.callMe {
System.print("This is the body!")
}
</pre>
Note that we'll see a shorthand syntax for creating a function below.
Here we're invoking the `callMe` method on `blondie`. We're passing one
argument, a function whose body is the
following [block](syntax.html#blocks)&mdash;everything between that pair of
curly braces.
Methods that take a block argument receive it as a normal parameter. `callMe`
could be defined like so:
<pre class="snippet">
class Blondie {
callMe(fn) {
// Call it...
}
}
var blondie = Blondie.new()
</pre>
A method can take other arguments in addition to the block. They appear before
the block just like a regular argument list. For example:
<pre class="snippet">
blondie.callMeAt(867, 5309) {
System.print("This is the body!")
}
</pre>
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:
<pre class="snippet">
var someFn = // Get a function...
blondie.callMe(someFn)
</pre>
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:
<pre class="snippet">
var someFn = Fn.new {
System.print("Hi!")
}
</pre>
As you can see it takes a block argument too! All the constructor does it
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:
<pre class="snippet">
class Blondie {
callMe(fn) {
fn.call()
}
}
</pre>
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.
<pre class="snippet">
class FakeFn {
call() {
System.print("I'm feeling functional!")
}
}
blondie.callMe(FakeFn.new())
</pre>
## Function parameters
Of course, functions aren't very useful if you can't pass values to them. The
function above takes no arguments. To change that, you can provide a parameter
list surrounded by `|` immediately after the opening brace of the body.
To pass arguments to the function, pass them to the `call` method:
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:
<pre class="snippet">
var sayMessage = Fn.new {|recipient, message|
System.print("message for %(recipient): %(message)")
blondie.callMe {|first, last|
System.print("Hi, " + first + " " + last + "!")
}
</pre>
sayMessage.call("Bob", "Good day!")
Here we're passing a function to `callMe` that takes two parameters, `first` and
`last`. They are passed to the function when it's called:
<pre class="snippet">
class Blondie {
callMe(fn) {
fn.call("Debbie", "Harry")
}
}
</pre>
It's an error to call a function with fewer arguments than its parameter list
@ -62,14 +148,6 @@ Fn.new {
}
</pre>
The return value is handed back to you when using `call`:
<pre class="snippet">
var fn = Fn.new { "some value" }
var result = fn.call()
System.print(result) //> some value
</pre>
## Closures
As you expect, functions are closures&mdash;they can access variables defined
@ -97,146 +175,6 @@ System.print(counter.call()) //> 2
System.print(counter.call()) //> 3
</pre>
## Callable classes
Because `Fn` is a class, and responds to `call()`, any class can respond to
`call()` and be used in place of a function. This is particularly handy when
the function is passed to a method to be called, like a callback or event.
<pre class="snippet">
class Callable {
construct new() {}
call(name, version) {
System.print("called %(name) with version %(version)")
}
}
var fn = Callable.new()
fn.call("wren", "0.4.0")
</pre>
## Block arguments
Very frequently, functions are passed to methods to be called. There are
countless examples of this in Wren, like [list](lists.html) can be filtered
using a method `where` which accepts a function:
<pre class="snippet">
var list = [1, 2, 3, 4, 5]
var filtered = list.where(Fn.new {|value| value > 3 })
System.print(filtered.toList) //> [4, 5]
</pre>
This syntax is a bit less fun to read and write, so Wren implements the
_block argument_ concept. When a function is being passed to a method,
and is the last argument to the method, it can use a shorter syntax:
_just the block part_.
Let's use a block argument for `list.where`, it's the last (only) argument:
<pre class="snippet">
var list = [1, 2, 3, 4, 5]
var filtered = list.where {|value| value > 3 }
System.print(filtered.toList) //> [4, 5]
</pre>
We've seen this before in a previous page using `map` and `where`:
<pre class="snippet">
numbers.map {|n| n * 2 }.where {|n| n < 100 }
</pre>
## Block argument example
Let's look at a complete example, so we can see both ends.
Here's a fictional class for something that will call a function
when a click event is sent to it. It allows us to pass just a
function and assume the left mouse button, or to pass a button and a function.
<pre class="snippet">
class Clickable {
construct new() {
_fn = null
_button = 0
}
onClick(fn) {
_fn = fn
}
onClick(button, fn) {
_button = button
_fn = fn
}
fireEvent(button) {
if(_fn && button == _button) {
_fn.call(button)
}
}
}
</pre>
Now that we've got the clickable class, let's use it.
We'll start by using the method that accepts just a function
because we're fine with it just being the default left mouse button.
<pre class="snippet">
var link = Clickable.new()
link.onClick {|button|
System.print("I was clicked by button %(button)")
}
// send a left mouse click
// normally this would happen from elsewhere
link.fireEvent(0) //> I was clicked by button 0
</pre>
Now let's try with the extra button argument:
<pre class="snippet">
var contextMenu = Clickable.new()
contextMenu.onClick(1) {|button|
System.print("I was right-clicked")
}
link.fireEvent(0) //> (nothing happened)
link.fireEvent(1) //> I was right-clicked
</pre>
Notice that we still pass the other arguments normally,
it's only the last argument that is special.
**Just a regular function**
Block arguments are purely syntax sugar for creating a function and passing it
in one little blob of syntax. These two are equivalent:
<pre class="snippet">
onClick(Fn.new { System.print("clicked") })
onClick { System.print("clicked") }
</pre>
And this is just as valid:
<pre class="snippet">
var onEvent = Fn.new {|button|
System.print("clicked by button %(button)")
}
onClick(onEvent)
onClick(1, onEvent)
</pre>
**Fn.new**
As you may have noticed by now, `Fn` accepts a block argument for the `Fn.new`.
All the constructor does is return that argument right back to you!
<br><hr>
<a class="right" href="classes.html">Classes &rarr;</a>
<a href="variables.html">&larr; Variables</a>

View File

@ -59,9 +59,7 @@ Since it has no dependencies this is simple, all the code in `src/` comes along.
If you want an even simpler way, there's an 'amalgamated' build (often called `blob`, or `unity` builds.).
This is _all of the wren source code in one file_.
This file can be generated by running `python3 util/generate_amalgamation.py > build/wren.c`,
which saves the generated output in `build/wren.c`.
This file can be generated by running `python3 util/generate_amalgamation.py`, and the generated output will be in `build/wren.c`.
Include `build/wren.c` and `src/include/wren.h` in your project code and you're good to go.
<small>Ideally later we can automate generating this and include it in the repo.</small>

View File

@ -20,23 +20,23 @@ element you want. Like most languages, indexes start at zero:
[subscript operator]: method-calls.html#subscripts
<pre class="snippet">
var trees = ["cedar", "birch", "oak", "willow"]
System.print(trees[0]) //> cedar
System.print(trees[1]) //> birch
var hirsute = ["sideburns", "porkchops", "'stache", "goatee"]
System.print(hirsute[0]) //> sideburns
System.print(hirsute[1]) //> porkchops
</pre>
Negative indices counts backwards from the end:
<pre class="snippet">
System.print(trees[-1]) //> willow
System.print(trees[-2]) //> oak
System.print(hirsute[-1]) //> goatee
System.print(hirsute[-2]) //> 'stache
</pre>
It's a runtime error to pass an index outside of the bounds of the list. If you
don't know what those bounds are, you can find out using count:
<pre class="snippet">
System.print(trees.count) //> 4
System.print(hirsute.count) //> 4
</pre>
## Slices and ranges
@ -45,7 +45,7 @@ Sometimes you want to copy a chunk of elements from a list. You can do that by
passing a [range](values.html#ranges) to the subscript operator, like so:
<pre class="snippet">
System.print(trees[1..2]) //> [birch, oak]
System.print(hirsute[1..2]) //> [porkchops, 'stache]
</pre>
This returns a new list containing the elements of the original list whose
@ -56,7 +56,7 @@ Negative bounds also work like they do when passing a single number, so to copy
a list, you can just do:
<pre class="snippet">
trees[0..-1]
hirsute[0..-1]
</pre>
## Adding elements
@ -65,22 +65,22 @@ Lists are *mutable*, meaning their contents can be changed. You can swap out an
existing element in the list using the subscript setter:
<pre class="snippet">
trees[1] = "spruce"
System.print(trees[1]) //> spruce
hirsute[1] = "muttonchops"
System.print(hirsute[1]) //> muttonchops
</pre>
It's an error to set an element that's out of bounds. To grow a list, you can
use `add` to append a single item to the end:
<pre class="snippet">
trees.add("maple")
System.print(trees.count) //> 5
hirsute.add("goatee")
System.print(hirsute.count) //> 5
</pre>
You can insert a new element at a specific position using `insert`:
<pre class="snippet">
trees.insert(2, "hickory")
hirsute.insert(2, "soul patch")
</pre>
The first argument is the index to insert at, and the second is the value to
@ -113,38 +113,26 @@ System.print(combined) //> [a, b, c, d, e, f]
## Removing elements
The opposite of `insert` is `removeAt`. It removes a single element from a
given position in the list.
To remove a specific _value_ instead, use `remove`. The first value that
matches using regular equality will be removed.
In both cases, all following items are shifted up to fill in the gap.
given position in the list. All following items are shifted up to fill in the
gap:
<pre class="snippet">
var letters = ["a", "b", "c", "d"]
letters.removeAt(1)
System.print(letters) //> [a, c, d]
letters.remove("a")
System.print(letters) //> [c, d]
</pre>
Both the `remove` and `removeAt` method return the removed item:
The `removeAt` method returns the removed item:
<pre class="snippet">
System.print(letters.removeAt(1)) //> c
</pre>
If `remove` couldn't find the value in the list, it returns null:
<pre class="snippet">
System.print(letters.remove("not found")) //> null
</pre>
If you want to remove everything from the list, you can clear it:
<pre class="snippet">
trees.clear()
System.print(trees) //> []
hirsute.clear()
System.print(hirsute) //> []
</pre>
<br><hr>

View File

@ -9,26 +9,22 @@ curly braces. Each entry is a key and a value separated by a colon:
<pre class="snippet">
{
"maple": "Sugar Maple (Acer Saccharum)",
"larch": "Alpine Larch (Larix Lyallii)",
"oak": "Red Oak (Quercus Rubra)",
"fir": "Fraser Fir (Abies Fraseri)"
"George": "Harrison",
"John": "Lennon",
"Paul": "McCartney",
"Ringo": "Starr"
}
</pre>
This creates a map that associates a type of tree (key) to a specific
tree within that family (value). Syntactically, in a map literal, keys
can be any literal, a variable name, or a parenthesized expression.
Values can be any expression. Here, we're using string literals for both keys
and values.
This creates a map that associates the first name of each Beatle with his last
name. Syntactically, in a map literal, keys can be any literal, a variable
name, or a parenthesized expression. Values can be any expression. Here, we're
using string literals for both keys and values.
*Semantically*, values can be any object, and multiple keys may map to the same
value.
Keys have a few limitations. They must be one of the immutable built-in
value. Keys have a few limitations. They must be one of the immutable built-in
[value types][] in Wren. That means a number, string, range, bool, or `null`.
You can also use a [class object][] as a key (not an instance of that class,
the actual class itself).
You can also use a [class object][] as a key.
[value types]: values.html
[class object]: classes.html
@ -71,12 +67,12 @@ doesn't necessarily mean the key wasn't found.
To tell definitively if a key exists, you can call `containsKey()`:
<pre class="snippet">
var capitals = {"Georgia": null}
var belief = {"nihilism": null}
System.print(capitals["Georgia"]) //> null (though key exists)
System.print(capitals["Idaho"]) //> null
System.print(capitals.containsKey("Georgia")) //> true
System.print(capitals.containsKey("Idaho")) //> false
System.print(belief["nihilism"]) //> null (though key exists)
System.print(belief["solipsism"]) //> null
System.print(belief.containsKey("nihilism")) //> true
System.print(belief.containsKey("solipsism")) //> false
</pre>
You can see how many entries a map contains using `count`:
@ -117,38 +113,16 @@ System.print(capitals.count) //> 0
The subscript operator works well for finding values when you know the key
you're looking for, but sometimes you want to see everything that's in the map.
You can use a regular for loop to iterate the contents, and map exposes two
additional methods to access the contents: `keys` and `values`.
For that, map exposes two methods: `keys` and `values`.
The `keys` method on a map returns a [Sequence][] that [iterates][] over all of
the keys in the map, and the `values` method returns one that iterates over the values.
The first returns a [Sequence][] that [iterates][] over all of the keys in the
map, and the second returns one that iterates over the values.
[sequence]: modules/core/sequence.html
[iterates]: control-flow.html#the-iterator-protocol
Regardless of how you iterate, the *order* that things are iterated in
isn't defined. Wren makes no promises about what order keys and values are
iterated. All it promises is that every entry will appear exactly once.
**Iterating with for(entry in map)**
When you iterate a map with `for`, you'll be handed an _entry_, which contains
a `key` and a `value` field. That gives you the info for each element in the map.
<pre class="snippet">
var birds = {
"Arizona": "Cactus wren",
"Hawaii": "Nēnē",
"Ohio": "Northern Cardinal"
}
for (bird in birds) {
System.print("The state bird of %(bird.key) is %(bird.value)")
}
</pre>
**Iterating using the keys**
You can also iterate over the keys and use each to look up its value:
If you want to see all of the key-value pairs in a map, the easiest way is to
iterate over the keys and use each to look up its value:
<pre class="snippet">
var birds = {
@ -158,10 +132,15 @@ var birds = {
}
for (state in birds.keys) {
System.print("The state bird of %(state) is " + birds[state])
System.print("The state bird of " + state + " is " + birds[state])
}
</pre>
This program prints the three states and their birds. However, the *order*
that they are printed isn't defined. Wren makes no promises about what order
keys and values are iterated in when you use these methods. All it promises is
that every entry will appear exactly once.
<br><hr>
<a class="right" href="method-calls.html">Method Calls &rarr;</a>
<a href="lists.html">&larr; Lists</a>

View File

@ -52,9 +52,7 @@ In a language like Python or JavaScript, these would both call a single `int()`
method, which has some kind of "optional" parameter. The body of the method
figures out how many arguments were passed and uses control flow to handle the
two different behaviors. That means first parameter represents "max unless
another parameter was passed, in which case it's min".
This type of 'variadic' code isn't ideal, so Wren doesn't encourage it.
another parameter was passed, in which case it's min". Kind of gross.
In Wren, these are calls to two entirely separate methods, `int(_,_)` and
`int(_)`. This makes it easier to define "overloads" like this since you don't
@ -165,7 +163,7 @@ like mocks or proxies where you want an object to masquerade as a certain class.
## Subscripts
Another familiar syntax from math is *subscripting* using square brackets
Another familiar syntax from math class is *subscripting* using square brackets
(`[]`). It's handy for working with collection-like objects. For example:
<pre class="snippet">

View File

@ -42,17 +42,6 @@ if (thirsty) {
}
</pre>
If you need to import a variable under a different name, you can use
`import "..." for Name as OtherName`. This looks up the top-level variable
`Name` in *that* module, but declares a variable called `OtherName` in *this* module
with its value.
<pre class="snippet">
import "liquids" for Water //Water is now taken
import "beverages" for Coffee, Water as H2O, Tea
// var water = H2O.new()
</pre>
If you want to load a module, but not bind any variables from it, you can omit
the `for` clause:

View File

@ -174,93 +174,14 @@ System.print("Caught error: " + error)
If the called fiber raises an error, it can no longer be used.
### **try**(value)
Tries to run the fiber. If a runtime error occurs
in the called fiber, the error is captured and is returned as a string.
If the fiber is being
started for the first time, and its function takes a parameter, `value` is
passed to it.
<pre class="snippet">
var fiber = Fiber.new {|value|
value.badMethod
}
var error = fiber.try("just a string")
System.print("Caught error: " + error)
</pre>
If the called fiber raises an error, it can no longer be used.
### **transfer**()
Pauses execution of the current running fiber, and transfers control to this fiber.
[Read more][transfers] about the difference between `call` and `transfer`.
Unlike `call`, `transfer` doesn't track the origin of the transfer.
[transfers]: ../../concurrency.html#transferring-control
<pre class="snippet">
// keep hold of the fiber we start in
var main = Fiber.current
// create a new fiber, note it doesn't execute yet!
var fiber = Fiber.new {
System.print("inside 'fiber'") //> #2: from #1
main.transfer() //> #3: go back to 'main'
}
fiber.transfer() //> #1: print "inside 'fiber'" via #2
//> this fiber is now paused by #1
System.print("main") //> #4: prints "main", unpaused by #3
</pre>
**TODO**
### **transfer**(value)
Pauses execution of the current running fiber, and transfers control to this fiber.
Similar to `transfer`, but a value can be passed between the fibers.
<pre class="snippet">
// keep hold of the fiber we start in
var main = Fiber.current
// create a new fiber, note it doesn't execute yet
// also note that we're accepting a 'value' parameter
var fiber = Fiber.new {|value|
System.print("in 'fiber' = %(value)") //> #2: in 'fiber' = 5
var result = main.transfer("hello?") //> #3: send to 'message'
System.print("end 'fiber' = %(result)") //> #6: end 'fiber' = 32
}
var message = fiber.transfer(5) //> #1: send to 'value'
System.print("... %(message)") //> #4: ... hello?
fiber.transfer(32) //> #5: send to 'result'
</pre>
**TODO**
### **transferError**(error)
Transfer to this fiber, but set this fiber into an error state.
The `fiber.error` value will be populated with the value in `error`.
<pre class="snippet">
var A = Fiber.new {
System.print("transferred to A") //> #4
B.transferError("error!") //> #5
}
var B = Fiber.new {
System.print("started B") //> #2
A.transfer() //> #3
System.print("should not get here")
}
B.try() //> #1
System.print(B.error) //> #6: prints "error!" from #5
// B fiber can no longer be used
B.call() //> #7: Cannot call an aborted fiber.
</pre>
**TODO**

View File

@ -12,7 +12,7 @@ An indexable contiguous collection of elements. More details [here][lists].
Creates a new list with `size` elements, all set to `element`.
It is a runtime error if `size` is not a non-negative integer.
It is a runtime error if `size` is not a nonnegative integer.
### List.**new**()
@ -22,19 +22,7 @@ Creates a new empty list. Equivalent to `[]`.
### **add**(item)
Appends `item` to the end of the list. Returns the added item.
### **addAll**(other)
Appends each element of `other` in the same order to the end of the list. `other` must be [an iterable](../../control-flow.html#the-iterator-protocol).
<pre class="snippet">
var list = [0, 1, 2, 3, 4]
list.addAll([5, 6])
System.print(list) //> [0, 1, 2, 3, 4, 5, 6]
</pre>
Returns the added items.
Appends `item` to the end of the list.
### **clear**()
@ -44,16 +32,6 @@ Removes all elements from the list.
The number of elements in the list.
### **indexOf**(value)
Returns the index of `value` in the list, if found. If not found, returns -1.
<pre class="snippet">
var list = [0, 1, 2, 3, 4]
System.print(list.indexOf(3)) //> 3
System.print(list.indexOf(20)) //> -1
</pre>
### **insert**(index, item)
Inserts the `item` at `index` in the list.
@ -96,26 +74,6 @@ list.
[iterator protocol]: ../../control-flow.html#the-iterator-protocol
### **remove**(value)
Removes the first value found in the list that matches the given `value`,
using regular equality to compare them. All trailing elements
are shifted up to fill in where the removed element was.
<pre class="snippet">
var list = ["a", "b", "c", "d"]
list.remove("b")
System.print(list) //> [a, c, d]
</pre>
Returns the removed value, if found.
If the value is not found in the list, returns null.
<pre class="snippet">
System.print(["a", "b", "c"].remove("b")) //> b
System.print(["a", "b", "c"].remove("not found")) //> null
</pre>
### **removeAt**(index)
Removes the element at `index`. If `index` is negative, it counts backwards
@ -136,40 +94,6 @@ System.print(["a", "b", "c"].removeAt(1)) //> b
It is a runtime error if the index is not an integer or is out of bounds.
### **sort**(), **sort**(comparer)
Sorts the elements of a list in-place; altering the list. The default sort is implemented using the quicksort algorithm.
<pre class="snippet">
var list = [4, 1, 3, 2].sort()
System.print(list) //> [1, 2, 3, 4]
</pre>
A comparison function `comparer` can be provided to customise the element sorting. The comparison function must return a boolean value specifying the order in which elements should appear in the list.
The comparison function accepts two arguments `a` and `b`, two values to compare, and must return a boolean indicating the inequality between the arguments. If the function returns true, the first argument `a` will appear before the second `b` in the sorted results.
A compare function like `{|a, b| true }` will always put `a` before `b`. The default compare function is `{|a, b| a < b }`.
<pre class="snippet">
var list = [9, 6, 8, 7]
list.sort {|a, b| a < b}
System.print(list) //> [6, 7, 8, 9]
</pre>
It is a runtime error if `comparer` is not a function.
### **swap**(index0, index1)
Swaps values inside the list around. Puts the value from `index0` in `index1`,
and the value from `index1` at `index0` in the list.
<pre class="snippet">
var list = [0, 1, 2, 3, 4]
list.swap(0, 3)
System.print(list) //> [3, 1, 2, 0, 4]
</pre>
### **[**index**]** operator
Gets the element at `index`. If `index` is negative, it counts backwards from
@ -180,17 +104,7 @@ var list = ["a", "b", "c"]
System.print(list[1]) //> b
</pre>
If `index` is a [Range](range.html), a new list is populated from the elements
in the range.
<pre class="snippet">
var list = ["a", "b", "c"]
System.print(list[0..1]) //> [a, b]
</pre>
You can use `list[0..-1]` to shallow-copy a list.
It is a runtime error if the index is not an integer or range, or is out of bounds.
It is a runtime error if the index is not an integer or is out of bounds.
### **[**index**]=**(item) operator
@ -205,23 +119,13 @@ System.print(list) //> [a, new, c]
It is a runtime error if the index is not an integer or is out of bounds.
### **+**(other) operator
## **+**(other) operator
Appends a list to the end of the list (concatenation). `other` must be [an iterable](../../control-flow.html#the-iterator-protocol).
Appends a list to the end of the list (concatenation). `other` must be a `List`.
<pre class="snippet">
var letters = ["a", "b", "c"]
var other = ["d", "e", "f"]
var combined = letters + other
System.print(combined) //> [a, b, c, d, e, f]
</pre>
### **\***(count) operator
Creates a new list by repeating this one ```count``` times. It is a runtime error if ```count``` is not a non-negative integer.
<pre class="snippet">
var digits = [1, 2]
var tripleDigits = digits * 3
System.print(tripleDigits) //> [1, 2, 1, 2, 1, 2]
</pre>
</pre>

View File

@ -1,15 +1,7 @@
^title Map Class
Extends [Sequence](sequence.html).
An associative collection that maps keys to values. More details [here](../../maps.html).
## Static Method
### Map.**new**()
Creates a new empty map. Equivalent to `{}`.
## Methods
### **clear**()
@ -64,22 +56,3 @@ replaces the previous association.
It is a runtime error if the key is not a [Bool](bool.html),
[Class](class.html), [Null](null.html), [Num](num.html), [Range](range.html),
or [String](string.html).
### **iterate**(iterator), **iteratorValue**(iterator)
Implements the [iterator protocol][] for iterating over the keys and values of a map at the same time.
[iterator protocol]: ../../control-flow.html#the-iterator-protocol
When a map (as opposed to its keys or values separately) is iterated over, each key/value pair is wrapped in a `MapEntry` object. `MapEntry` is a small helper class which has read-only `key` and `value` properties and a familiar `toString` representation.
<pre class="snippet">
var map = {"paul": "mccartney"}
for (entry in map) {
System.print(entry.type) // MapEntry
System.print(entry.key + " " + entry.value) // paul mccartney
System.print(entry) // paul:mccartney
}
</pre>
All map entries will be iterated over, but may be in any order, and may even change between invocations of Wren.

View File

@ -4,7 +4,7 @@
### **!** operator
Returns `true`, since `null` is considered [false](../../control-flow.html#truth).
Returns `true`, since `null` is considered [false](../control-flow.html#truth).
<pre class="snippet">
System.print(!null) //> true

View File

@ -9,24 +9,10 @@ Attempts to parse `value` as a decimal literal and return it as an instance of
It is a runtime error if `value` is not a string.
### Num.**infinity**
The value of &infin;.
### Num.**nan**
One value representing a NaN.
Provides a default NaN number suitable for the vm internal values.
### Num.**pi**
The value of &pi;.
### Num.**tau**
The value of &tau;. This is equivalent to ```2 * Num.pi```.
### Num.**largest**
The largest representable numeric value.
@ -35,17 +21,6 @@ The largest representable numeric value.
The smallest positive representable numeric value.
### Num.**maxSafeInteger**
The largest integer that Wren can safely represent. It's a constant value of `9007199254740991`.
This is relevant because Wren uses double precision [floating-point format](https://en.wikipedia.org/wiki/IEEE_floating_point)
for numbers, which can only safely represent integers between <code>-(2<sup>53</sup> - 1)</code> and <code>2<sup>53</sup> - 1</code>.
### Num.**minSafeInteger**
The smallest integer Wren can safely represent. It's a constant value of `-9007199254740991`.
## Methods
### **abs**
@ -73,10 +48,6 @@ The arc tangent of the number.
The arc tangent of the number when divided by `x`, using the signs of the two
numbers to determine the quadrant of the result.
### **cbrt**
The cube root of the number.
### **ceil**
Rounds the number up to the nearest integer.
@ -99,17 +70,6 @@ System.print(1.5.floor) //> 1
System.print((-3.2).floor) //> -4
</pre>
### **fraction**
The fractional part of a number i.e. the part after any decimal point.
The returned value has the same sign as `this`.
<pre class="snippet">
System.print(1.5.fraction) //> 0.5
System.print((-3.2).fraction) //> -0.2
</pre>
### **isInfinity**
Whether the number is positive or negative infinity or not.
@ -146,20 +106,6 @@ The binary (base-2) logarithm of the number. Returns `nan` if the base is negati
The exponential `e` (Eulers number) raised to the number. This: `eⁿ`.
### **min**(other)
Returns the minimum value when comparing this number and `other`.
### **max**(other)
Returns the maximum value when comparing this number and `other`.
### **clamp**(min, max)
Clamps a number into the range of `min` and `max`. If this number is less than min,
`min` is returned. If bigger than `max`, `max` is returned. Otherwise, the number
itself is returned.
### **pow**(power)
Raises this number (the base) to `power`. Returns `nan` if the base is negative.
@ -190,21 +136,6 @@ The square root of the number. Returns `nan` if the number is negative.
The tangent of the number.
### **toString**
The string representation of the number.
### **truncate**
Rounds the number to the nearest integer towards zero.
It is therefore equivalent to `floor` if the number is non-negative or `ceil` if it is negative.
<pre class="snippet">
System.print(1.5.truncate) //> 1
System.print((-3.2).truncate) //> -3
</pre>
### **-** operator
Negates the number.
@ -256,24 +187,6 @@ unsigned values. The result is then a 32-bit unsigned number where each bit is
It is a runtime error if `other` is not a number.
### **^**(other) operator
Performs bitwise exclusive or on the number. Both numbers are first converted to 32-bit unsigned values. The result is then a 32-bit unsigned number where each bit is `true` only where the corresponding bits of one (but not both) inputs were `true`. Each bit is therefore `false` if the corresponding bits of both inputs were either both `true` or both `false`.
It is a runtime error if `other` is not a number.
### **&lt;&lt;**(other) operator
Performs a bitwise left shift on the number. Internally, both numbers are first converted to 32-bit unsigned values and C's left shift operator is then applied to them.
It is a runtime error if `other` is not a number.
### **&gt;&gt;**(other) operator
Performs a bitwise right shift on the number. Internally, both numbers are first converted to 32-bit unsigned values and C's right shift operator is then applied to them.
It is a runtime error if `other` is not a number.
### **..**(other) operator
Creates a [Range](range.html) representing a consecutive range of numbers
@ -296,4 +209,4 @@ var range = 1.2...3.4
System.print(range.min) //> 1.2
System.print(range.max) //> 3.4
System.print(range.isInclusive) //> false
</pre>
</pre>

View File

@ -40,15 +40,6 @@ var hPosition = metalBand.indexOf("h")
System.print(metalBand[hPosition]) //> h
</pre>
A string can also be indexed with a [Range](range.html), which will return a
new string as a substring of the original.
<pre class="snippet">
var example = "hello wren"
System.print(example[0...5]) //> hello
System.print(example[-4..-1]) //> wren
</pre>
If you want to work with a string as a sequence numeric code points, call the
`codePoints` getter. It returns a [Sequence](sequence.html) that decodes UTF-8
and iterates over the code points, returning each as a number.
@ -255,12 +246,6 @@ Returns a new string that concatenates this string and `other`.
It is a runtime error if `other` is not a string.
### *****(count) operator
Returns a new string that contains this string repeated `count` times.
It is a runtime error if `count` is not a positive integer.
### **==**(other) operator
Checks if the string is equal to `other`.

View File

@ -48,8 +48,3 @@ System.write(4 + 5) //> 9
In the above example, the result of `4 + 5` is printed, and then the prompt is
printed on the same line because no newline character was printed afterwards.
### System.**writeAll**(sequence)
Iterates over `sequence` and prints each element, but does not print a newline
character afterwards. Each element is converted to a string by calling `toString` on it.

View File

@ -15,7 +15,6 @@ It must be imported from the [random][] module:
<pre class="snippet">
import "random" for Random
</pre>
[random]: ../

File diff suppressed because one or more lines are too long

View File

@ -607,8 +607,8 @@ var wasmMemory;
// In the wasm backend, we polyfill the WebAssembly object,
// so this creates a (non-native-wasm) table for us.
var wasmTable = new WebAssembly.Table({
'initial': 203,
'maximum': 203 + 0,
'initial': 191,
'maximum': 191 + 0,
'element': 'anyfunc'
});
@ -1229,11 +1229,11 @@ function updateGlobalBufferAndViews(buf) {
}
var STATIC_BASE = 1024,
STACK_BASE = 5271568,
STACK_BASE = 5271824,
STACKTOP = STACK_BASE,
STACK_MAX = 28688,
DYNAMIC_BASE = 5271568,
DYNAMICTOP_PTR = 28528;
STACK_MAX = 28944,
DYNAMIC_BASE = 5271824,
DYNAMICTOP_PTR = 28784;
assert(STACK_BASE % 16 === 0, 'stack must start aligned');
assert(DYNAMIC_BASE % 16 === 0, 'heap must start aligned');
@ -1817,7 +1817,7 @@ var ASM_CONSTS = {
// STATICTOP = STATIC_BASE + 27664;
// STATICTOP = STATIC_BASE + 27920;
/* global initializers */ __ATINIT__.push({ func: function() { ___wasm_call_ctors() } });
@ -1869,37 +1869,13 @@ var ASM_CONSTS = {
abort('stack overflow')
}
function _clock() {
if (_clock.start === undefined) _clock.start = Date.now();
return ((Date.now() - _clock.start) * (1000000 / 1000))|0;
}
function _emscripten_get_sbrk_ptr() {
return 28528;
}
function _emscripten_memcpy_big(dest, src, num) {
HEAPU8.copyWithin(dest, src, src + num);
}
function _emscripten_get_heap_size() {
return HEAPU8.length;
function ___setErrNo(value) {
if (Module['___errno_location']) HEAP32[((Module['___errno_location']())>>2)]=value;
else err('failed to set errno from JS');
return value;
}
function abortOnCannotGrowMemory(requestedSize) {
abort('Cannot enlarge memory arrays to size ' + requestedSize + ' bytes (OOM). Either (1) compile with -s INITIAL_MEMORY=X with X higher than the current value ' + HEAP8.length + ', (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ');
}function _emscripten_resize_heap(requestedSize) {
abortOnCannotGrowMemory(requestedSize);
}
function _exit(status) {
// void _exit(int status);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html
exit(status);
}
var PATH={splitPath:function(filename) {
var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
@ -1987,11 +1963,62 @@ var ASM_CONSTS = {
if (low >= 0) assert(high === 0);
else assert(high === -1);
return low;
}};function _fd_close(fd) {
}};function ___sys_fcntl64(fd, cmd, varargs) {SYSCALLS.varargs = varargs;
return 0;
}
function ___sys_ioctl(fd, op, varargs) {SYSCALLS.varargs = varargs;
return 0;
}
function ___sys_open(path, flags, varargs) {SYSCALLS.varargs = varargs;
abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM')}
function _clock() {
if (_clock.start === undefined) _clock.start = Date.now();
return ((Date.now() - _clock.start) * (1000000 / 1000))|0;
}
function _emscripten_get_sbrk_ptr() {
return 28784;
}
function _emscripten_memcpy_big(dest, src, num) {
HEAPU8.copyWithin(dest, src, src + num);
}
function _emscripten_get_heap_size() {
return HEAPU8.length;
}
function abortOnCannotGrowMemory(requestedSize) {
abort('Cannot enlarge memory arrays to size ' + requestedSize + ' bytes (OOM). Either (1) compile with -s INITIAL_MEMORY=X with X higher than the current value ' + HEAP8.length + ', (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ');
}function _emscripten_resize_heap(requestedSize) {
abortOnCannotGrowMemory(requestedSize);
}
function _exit(status) {
// void _exit(int status);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html
exit(status);
}
function _fd_close(fd) {
abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM');
return 0;
}
function _fd_read(fd, iov, iovcnt, pnum) {
var stream = SYSCALLS.getStreamFromFD(fd);
var num = SYSCALLS.doReadv(stream, iov, iovcnt);
HEAP32[((pnum)>>2)]=num
return 0;
}
function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {
abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM')}
@ -2070,7 +2097,7 @@ function intArrayToString(array) {
// ASM_LIBRARY EXTERN PRIMITIVES: Math_floor,Math_ceil
var asmGlobalArg = {};
var asmLibraryArg = { "__handle_stack_overflow": ___handle_stack_overflow, "clock": _clock, "emscripten_get_sbrk_ptr": _emscripten_get_sbrk_ptr, "emscripten_memcpy_big": _emscripten_memcpy_big, "emscripten_resize_heap": _emscripten_resize_heap, "exit": _exit, "fd_close": _fd_close, "fd_seek": _fd_seek, "fd_write": _fd_write, "memory": wasmMemory, "round": _round, "setTempRet0": _setTempRet0, "table": wasmTable, "time": _time };
var asmLibraryArg = { "__handle_stack_overflow": ___handle_stack_overflow, "__sys_fcntl64": ___sys_fcntl64, "__sys_ioctl": ___sys_ioctl, "__sys_open": ___sys_open, "clock": _clock, "emscripten_get_sbrk_ptr": _emscripten_get_sbrk_ptr, "emscripten_memcpy_big": _emscripten_memcpy_big, "emscripten_resize_heap": _emscripten_resize_heap, "exit": _exit, "fd_close": _fd_close, "fd_read": _fd_read, "fd_seek": _fd_seek, "fd_write": _fd_write, "memory": wasmMemory, "round": _round, "setTempRet0": _setTempRet0, "table": wasmTable, "time": _time };
var asm = createWasm();
Module["asm"] = asm;
/** @type {function(...*):?} */
@ -2158,10 +2185,10 @@ var dynCall_vii = Module["dynCall_vii"] = function() {
};
/** @type {function(...*):?} */
var dynCall_viii = Module["dynCall_viii"] = function() {
var dynCall_iii = Module["dynCall_iii"] = function() {
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
return Module["asm"]["dynCall_viii"].apply(null, arguments)
return Module["asm"]["dynCall_iii"].apply(null, arguments)
};
/** @type {function(...*):?} */
@ -2171,13 +2198,6 @@ var dynCall_iiii = Module["dynCall_iiii"] = function() {
return Module["asm"]["dynCall_iiii"].apply(null, arguments)
};
/** @type {function(...*):?} */
var dynCall_iii = Module["dynCall_iii"] = function() {
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
return Module["asm"]["dynCall_iii"].apply(null, arguments)
};
/** @type {function(...*):?} */
var dynCall_vi = Module["dynCall_vi"] = function() {
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
@ -2185,13 +2205,6 @@ var dynCall_vi = Module["dynCall_vi"] = function() {
return Module["asm"]["dynCall_vi"].apply(null, arguments)
};
/** @type {function(...*):?} */
var dynCall_ii = Module["dynCall_ii"] = function() {
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
return Module["asm"]["dynCall_ii"].apply(null, arguments)
};
/** @type {function(...*):?} */
var dynCall_jiji = Module["dynCall_jiji"] = function() {
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
@ -2199,6 +2212,13 @@ var dynCall_jiji = Module["dynCall_jiji"] = function() {
return Module["asm"]["dynCall_jiji"].apply(null, arguments)
};
/** @type {function(...*):?} */
var dynCall_ii = Module["dynCall_ii"] = function() {
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
return Module["asm"]["dynCall_ii"].apply(null, arguments)
};
/** @type {function(...*):?} */
var dynCall_iidiiii = Module["dynCall_iidiiii"] = function() {
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');

Binary file not shown.

View File

@ -43,7 +43,7 @@ One way to get a quick feel for a language's style is to see what words it
reserves. Here's what Wren has:
<pre class="snippet">
as break class construct continue else false for foreign if import
break class construct else false for foreign if import
in is null return static super this true var while
</pre>
@ -149,7 +149,7 @@ put a newline in there:
</pre>
Using an initial newline after the `{` does feel a little weird or magical, but
newlines are already significant in Wren, so it's not totally unreasonable. The nice
newlines are already significant in Wren, so it's not totally crazy. The nice
thing about this syntax as opposed to something like `=>` is that the *end* of
the block has an explicit delimiter. That helps when chaining:

View File

@ -41,8 +41,8 @@
<li><a href="method-calls.html">Method Calls</a></li>
<li><a href="control-flow.html">Control Flow</a></li>
<li><a href="variables.html">Variables</a></li>
<li><a href="classes.html">Classes</a></li>
<li><a href="functions.html">Functions</a></li>
<li><a href="classes.html">Classes</a></li>
<li><a href="concurrency.html">Concurrency</a></li>
<li><a href="error-handling.html">Error Handling</a></li>
<li><a href="modularity.html">Modularity</a></li>
@ -90,8 +90,8 @@
<td>
<ul>
<li><a href="variables.html">Variables</a></li>
<li><a href="classes.html">Classes</a></li>
<li><a href="functions.html">Functions</a></li>
<li><a href="classes.html">Classes</a></li>
<li><a href="concurrency.html">Concurrency</a></li>
<li><a href="error-handling.html">Error Handling</a></li>
<li><a href="modularity.html">Modularity</a></li>

View File

@ -42,8 +42,8 @@
<li><a href="../method-calls.html">Method Calls</a></li>
<li><a href="../control-flow.html">Control Flow</a></li>
<li><a href="../variables.html">Variables</a></li>
<li><a href="../classes.html">Classes</a></li>
<li><a href="../functions.html">Functions</a></li>
<li><a href="../classes.html">Classes</a></li>
<li><a href="../concurrency.html">Concurrency</a></li>
<li><a href="../error-handling.html">Error Handling</a></li>
<li><a href="../modularity.html">Modularity</a></li>
@ -91,8 +91,8 @@
<td>
<ul>
<li><a href="../variables.html">Variables</a></li>
<li><a href="../classes.html">Classes</a></li>
<li><a href="../functions.html">Functions</a></li>
<li><a href="../classes.html">Classes</a></li>
<li><a href="../concurrency.html">Concurrency</a></li>
<li><a href="../error-handling.html">Error Handling</a></li>
<li><a href="../modularity.html">Modularity</a></li>

View File

@ -30,7 +30,6 @@ from other languages:
0.0314159e02
0.0314159e+02
314.159e-02
0xcaffe2
</pre>
Numbers are instances of the [Num][] class.
@ -50,16 +49,6 @@ String literals are surrounded in double quotes:
"hi there"
</pre>
They can also span multiple lines:
<pre class="snippet">
"hi
there,
again"
</pre>
### Escaping
A handful of escape characters are supported:
<pre class="snippet">
@ -69,23 +58,11 @@ A handful of escape characters are supported:
"\%" // A percent sign.
"\a" // Alarm beep. (Who uses this?)
"\b" // Backspace.
"\e" // ESC character.
"\f" // Formfeed.
"\n" // Newline.
"\r" // Carriage return.
"\t" // Tab.
"\v" // Vertical tab.
"\x48" // Unencoded byte (2 hex digits)
"\u0041" // Unicode code point (4 hex digits)
"\U0001F64A" // Unicode code point (8 hex digits)
</pre>
A `\x` followed by two hex digits specifies a single unencoded byte:
<pre class="snippet">
System.print("\x48\x69\x2e") //> Hi.
</pre>
A `\u` followed by four hex digits can be used to specify a Unicode code point:
@ -101,6 +78,12 @@ of the basic multilingual plane, like all-important emoji:
System.print("\U0001F64A\U0001F680") //> 🙊🚀
</pre>
A `\x` followed by two hex digits specifies a single unencoded byte:
<pre class="snippet">
System.print("\x48\x69\x2e") //> Hi.
</pre>
Strings are instances of class [String][].
[string]: modules/core/string.html
@ -125,63 +108,6 @@ System.print("wow %((1..3).map {|n| n * n}.join())") //> wow 149
An interpolated expression can even contain a string literal which in turn has
its own nested interpolations, but doing that gets unreadable pretty quickly.
### Raw strings
A string literal can also be created using triple quotes `"""` which is
parsed as a raw string. A raw string is no different
from any other string, it's just parsed in a different way.
**Raw strings do not process escapes and do not apply any interpolation**.
<pre class="snippet">
"""hi there"""
</pre>
When a raw string spans multiple lines and a triple quote is on it's own line,
any whitespace on that line will be ignored. This means the opening and closing
lines are not counted as part of the string when the triple quotes are separate lines,
as long as they only contain whitespace (spaces + tabs).
<pre class="snippet">
"""
Hello world
"""
</pre>
The resulting value in the string above has no newlines or trailing whitespace.
Note the spaces in front of the Hello are preserved.
<pre class="snippet">
Hello world
</pre>
A raw string will be parsed exactly as is in the file, unmodified.
This means it can contain quotes, invalid syntax, other data formats
and so on without being modified by Wren.
<pre class="snippet">
"""
{
"hello": "wren",
"from" : "json"
}
"""
</pre>
One more example, embedding wren code inside a string safely.
<pre class="snippet">
"""
A markdown string with embedded wren code example.
class Example {
construct code() {
//
}
}
"""
</pre>
## Ranges
A range is a little object that represents a consecutive range of numbers. They
@ -205,16 +131,12 @@ This creates a range from four to six *not* including six itself. Ranges are
commonly used for [iterating](control-flow.html#for-statements) over a
sequences of numbers, but are useful in other places too. You can pass them to
a [list](lists.html)'s subscript operator to return a subset of the list, for
example, or on a String, the substring in that range:
example:
<pre class="snippet">
var list = ["a", "b", "c", "d", "e"]
var slice = list[1..3]
System.print(slice) //> [b, c, d]
var string = "hello wren"
var wren = string[-4..-1]
System.print(wren) //> wren
</pre>
Their class is [Range][].

View File

@ -31,7 +31,7 @@ System.print(a) //! "a" doesn't exist anymore.
</pre>
Variables defined at the top level of a script are *top-level* and are visible
to the [module](modularity.html) system. All other variables are *local*.
to the [module](modules.html) system. All other variables are *local*.
Declaring a variable in an inner scope with the same name as an outer one is
called *shadowing* and is not an error (although it's not something you likely
intend to do much).
@ -54,7 +54,7 @@ var a = "again" //! "a" is already declared.
## Assignment
After a variable has been declared, you can assign to it using `=`
After a variable has been declared, you can assign to it using `=`:
<pre class="snippet">
var a = 123

View File

@ -1,58 +0,0 @@
//For more details, visit https://wren.io/embedding/
#include <stdio.h>
#include "wren.h"
static void writeFn(WrenVM* vm, const char* text)
{
printf("%s", text);
}
void errorFn(WrenVM* vm, WrenErrorType errorType,
const char* module, const int line,
const char* msg)
{
switch (errorType)
{
case WREN_ERROR_COMPILE:
{
printf("[%s line %d] [Error] %s\n", module, line, msg);
} break;
case WREN_ERROR_STACK_TRACE:
{
printf("[%s line %d] in %s\n", module, line, msg);
} break;
case WREN_ERROR_RUNTIME:
{
printf("[Runtime Error] %s\n", msg);
} break;
}
}
int main()
{
WrenConfiguration config;
wrenInitConfiguration(&config);
config.writeFn = &writeFn;
config.errorFn = &errorFn;
WrenVM* vm = wrenNewVM(&config);
const char* module = "main";
const char* script = "System.print(\"I am running in a VM!\")";
WrenInterpretResult result = wrenInterpret(vm, module, script);
switch (result)
{
case WREN_RESULT_COMPILE_ERROR:
{ printf("Compile Error!\n"); } break;
case WREN_RESULT_RUNTIME_ERROR:
{ printf("Runtime Error!\n"); } break;
case WREN_RESULT_SUCCESS:
{ printf("Success!\n"); } break;
}
wrenFreeVM(vm);
}

View File

@ -7,11 +7,11 @@
// The Wren semantic version number components.
#define WREN_VERSION_MAJOR 0
#define WREN_VERSION_MINOR 4
#define WREN_VERSION_MINOR 3
#define WREN_VERSION_PATCH 0
// A human-friendly string representation of the version.
#define WREN_VERSION_STRING "0.4.0"
#define WREN_VERSION_STRING "0.3.0"
// A monotonically increasing numeric representation of the version number. Use
// this if you want to do range checks over versions.
@ -19,14 +19,6 @@
WREN_VERSION_MINOR * 1000 + \
WREN_VERSION_PATCH)
#ifndef WREN_API
#if defined(_MSC_VER) && defined(WREN_API_DLLEXPORT)
#define WREN_API __declspec( dllexport )
#else
#define WREN_API
#endif
#endif //WREN_API
// A single virtual machine for executing Wren code.
//
// Wren has no global state, so all state stored by a running interpreter lives
@ -55,7 +47,7 @@ typedef struct WrenHandle WrenHandle;
//
// - To free memory, [memory] will be the memory to free and [newSize] will be
// zero. It should return NULL.
typedef void* (*WrenReallocateFn)(void* memory, size_t newSize, void* userData);
typedef void* (*WrenReallocateFn)(void* memory, size_t newSize);
// A function callable from Wren code, but implemented in C.
typedef void (*WrenForeignMethodFn)(WrenVM* vm);
@ -74,13 +66,14 @@ typedef const char* (*WrenResolveModuleFn)(WrenVM* vm,
const char* importer, const char* name);
// Forward declare
struct WrenLoadModuleResult;
typedef struct WrenLoadModuleResult WrenLoadModuleResult;
// Called after loadModuleFn is called for module [name]. The original returned result
// is handed back to you in this callback, so that you can free memory if appropriate.
typedef void (*WrenLoadModuleCompleteFn)(WrenVM* vm, const char* name, struct WrenLoadModuleResult result);
typedef void (*WrenLoadModuleCompleteFn)(WrenVM* vm, const char* name, WrenLoadModuleResult result);
// The result of a loadModuleFn call.
// [length] is optional, a value of 0 means length is ignored.
// [source] is the source code for the module, or NULL if the module is not found.
// [onComplete] an optional callback that will be called once Wren is done with the result.
typedef struct WrenLoadModuleResult
@ -188,9 +181,9 @@ typedef struct
// Since Wren does not talk directly to the file system, it relies on the
// embedder to physically locate and read the source code for a module. The
// first time an import appears, Wren will call this and pass in the name of
// the module being imported. The method will return a result, which contains
// the source code for that module. Memory for the source is owned by the
// host application, and can be freed using the onComplete callback.
// the module being imported. The VM should return the soure code for that
// module. Memory for the source should be allocated using [reallocateFn] and
// Wren will take ownership over it.
//
// This will only be called once for any given module name. Wren caches the
// result internally so subsequent imports of the same module will use the
@ -296,32 +289,27 @@ typedef enum
WREN_TYPE_UNKNOWN
} WrenType;
// Get the current wren version number.
//
// Can be used to range checks over versions.
WREN_API int wrenGetVersionNumber();
// Initializes [configuration] with all of its default values.
//
// Call this before setting the particular fields you care about.
WREN_API void wrenInitConfiguration(WrenConfiguration* configuration);
void wrenInitConfiguration(WrenConfiguration* configuration);
// Creates a new Wren virtual machine using the given [configuration]. Wren
// will copy the configuration data, so the argument passed to this can be
// freed after calling this. If [configuration] is `NULL`, uses a default
// configuration.
WREN_API WrenVM* wrenNewVM(WrenConfiguration* configuration);
WrenVM* wrenNewVM(WrenConfiguration* configuration);
// Disposes of all resources is use by [vm], which was previously created by a
// call to [wrenNewVM].
WREN_API void wrenFreeVM(WrenVM* vm);
void wrenFreeVM(WrenVM* vm);
// Immediately run the garbage collector to free unused memory.
WREN_API void wrenCollectGarbage(WrenVM* vm);
void wrenCollectGarbage(WrenVM* vm);
// Runs [source], a string of Wren source code in a new fiber in [vm] in the
// context of resolved [module].
WREN_API WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
const char* source);
// Creates a handle that can be used to invoke a method with [signature] on
@ -332,7 +320,7 @@ WREN_API WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
//
// When you are done with this handle, it must be released using
// [wrenReleaseHandle].
WREN_API WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
// Calls [method], using the receiver and arguments previously set up on the
// stack.
@ -344,11 +332,11 @@ WREN_API WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
// signature.
//
// After this returns, you can access the return value from slot 0 on the stack.
WREN_API WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
// Releases the reference stored in [handle]. After calling this, [handle] can
// no longer be used.
WREN_API void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
// The following functions are intended to be called from foreign methods or
// finalizers. The interface Wren provides to a foreign method is like a
@ -388,7 +376,7 @@ WREN_API void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
// return, you get a very fast FFI.
// Returns the number of slots available to the current foreign method.
WREN_API int wrenGetSlotCount(WrenVM* vm);
int wrenGetSlotCount(WrenVM* vm);
// Ensures that the foreign method stack has at least [numSlots] available for
// use, growing the stack if needed.
@ -396,15 +384,15 @@ WREN_API int wrenGetSlotCount(WrenVM* vm);
// Does not shrink the stack if it has more than enough slots.
//
// It is an error to call this from a finalizer.
WREN_API void wrenEnsureSlots(WrenVM* vm, int numSlots);
void wrenEnsureSlots(WrenVM* vm, int numSlots);
// Gets the type of the object in [slot].
WREN_API WrenType wrenGetSlotType(WrenVM* vm, int slot);
WrenType wrenGetSlotType(WrenVM* vm, int slot);
// Reads a boolean value from [slot].
//
// It is an error to call this if the slot does not contain a boolean value.
WREN_API bool wrenGetSlotBool(WrenVM* vm, int slot);
bool wrenGetSlotBool(WrenVM* vm, int slot);
// Reads a byte array from [slot].
//
@ -416,19 +404,19 @@ WREN_API bool wrenGetSlotBool(WrenVM* vm, int slot);
// number of bytes in the array.
//
// It is an error to call this if the slot does not contain a string.
WREN_API const char* wrenGetSlotBytes(WrenVM* vm, int slot, int* length);
const char* wrenGetSlotBytes(WrenVM* vm, int slot, int* length);
// Reads a number from [slot].
//
// It is an error to call this if the slot does not contain a number.
WREN_API double wrenGetSlotDouble(WrenVM* vm, int slot);
double wrenGetSlotDouble(WrenVM* vm, int slot);
// Reads a foreign object from [slot] and returns a pointer to the foreign data
// stored with it.
//
// It is an error to call this if the slot does not contain an instance of a
// foreign class.
WREN_API void* wrenGetSlotForeign(WrenVM* vm, int slot);
void* wrenGetSlotForeign(WrenVM* vm, int slot);
// Reads a string from [slot].
//
@ -437,25 +425,25 @@ WREN_API void* wrenGetSlotForeign(WrenVM* vm, int slot);
// function returns, since the garbage collector may reclaim it.
//
// It is an error to call this if the slot does not contain a string.
WREN_API const char* wrenGetSlotString(WrenVM* vm, int slot);
const char* wrenGetSlotString(WrenVM* vm, int slot);
// Creates a handle for the value stored in [slot].
//
// This will prevent the object that is referred to from being garbage collected
// until the handle is released by calling [wrenReleaseHandle()].
WREN_API WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
// Stores the boolean [value] in [slot].
WREN_API void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
// Stores the array [length] of [bytes] in [slot].
//
// The bytes are copied to a new string within Wren's heap, so you can free
// memory used by them after this is called.
WREN_API void wrenSetSlotBytes(WrenVM* vm, int slot, const char* bytes, size_t length);
void wrenSetSlotBytes(WrenVM* vm, int slot, const char* bytes, size_t length);
// Stores the numeric [value] in [slot].
WREN_API void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
// Creates a new instance of the foreign class stored in [classSlot] with [size]
// bytes of raw storage and places the resulting object in [slot].
@ -466,16 +454,16 @@ WREN_API void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
// and then the constructor will be invoked when the allocator returns.
//
// Returns a pointer to the foreign object's data.
WREN_API void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size);
void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size);
// Stores a new empty list in [slot].
WREN_API void wrenSetSlotNewList(WrenVM* vm, int slot);
void wrenSetSlotNewList(WrenVM* vm, int slot);
// Stores a new empty map in [slot].
WREN_API void wrenSetSlotNewMap(WrenVM* vm, int slot);
void wrenSetSlotNewMap(WrenVM* vm, int slot);
// Stores null in [slot].
WREN_API void wrenSetSlotNull(WrenVM* vm, int slot);
void wrenSetSlotNull(WrenVM* vm, int slot);
// Stores the string [text] in [slot].
//
@ -483,72 +471,60 @@ WREN_API void wrenSetSlotNull(WrenVM* vm, int slot);
// memory used by it after this is called. The length is calculated using
// [strlen()]. If the string may contain any null bytes in the middle, then you
// should use [wrenSetSlotBytes()] instead.
WREN_API void wrenSetSlotString(WrenVM* vm, int slot, const char* text);
void wrenSetSlotString(WrenVM* vm, int slot, const char* text);
// Stores the value captured in [handle] in [slot].
//
// This does not release the handle for the value.
WREN_API void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
// Returns the number of elements in the list stored in [slot].
WREN_API int wrenGetListCount(WrenVM* vm, int slot);
int wrenGetListCount(WrenVM* vm, int slot);
// Reads element [index] from the list in [listSlot] and stores it in
// [elementSlot].
WREN_API void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);
// Sets the value stored at [index] in the list at [listSlot],
// to the value from [elementSlot].
WREN_API void wrenSetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);
void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);
// Takes the value stored at [elementSlot] and inserts it into the list stored
// at [listSlot] at [index].
//
// As in Wren, negative indexes can be used to insert from the end. To append
// an element, use `-1` for the index.
WREN_API void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot);
void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot);
// Returns the number of entries in the map stored in [slot].
WREN_API int wrenGetMapCount(WrenVM* vm, int slot);
int wrenGetMapCount(WrenVM* vm, int slot);
// Returns true if the key in [keySlot] is found in the map placed in [mapSlot].
WREN_API bool wrenGetMapContainsKey(WrenVM* vm, int mapSlot, int keySlot);
bool wrenGetMapContainsKey(WrenVM* vm, int mapSlot, int keySlot);
// Retrieves a value with the key in [keySlot] from the map in [mapSlot] and
// stores it in [valueSlot].
WREN_API void wrenGetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot);
void wrenGetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot);
// Takes the value stored at [valueSlot] and inserts it into the map stored
// at [mapSlot] with key [keySlot].
WREN_API void wrenSetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot);
void wrenSetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot);
// Removes a value from the map in [mapSlot], with the key from [keySlot],
// and place it in [removedValueSlot]. If not found, [removedValueSlot] is
// set to null, the same behaviour as the Wren Map API.
WREN_API void wrenRemoveMapValue(WrenVM* vm, int mapSlot, int keySlot,
void wrenRemoveMapValue(WrenVM* vm, int mapSlot, int keySlot,
int removedValueSlot);
// Looks up the top level variable with [name] in resolved [module] and stores
// it in [slot].
WREN_API void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
int slot);
// Looks up the top level variable with [name] in resolved [module],
// returns false if not found. The module must be imported at the time,
// use wrenHasModule to ensure that before calling.
WREN_API bool wrenHasVariable(WrenVM* vm, const char* module, const char* name);
// Returns true if [module] has been imported/resolved before, false if not.
WREN_API bool wrenHasModule(WrenVM* vm, const char* module);
// Sets the current fiber to be aborted, and uses the value in [slot] as the
// runtime error object.
WREN_API void wrenAbortFiber(WrenVM* vm, int slot);
void wrenAbortFiber(WrenVM* vm, int slot);
// Returns the user data associated with the WrenVM.
WREN_API void* wrenGetUserData(WrenVM* vm);
void* wrenGetUserData(WrenVM* vm);
// Sets user data associated with the WrenVM.
WREN_API void wrenSetUserData(WrenVM* vm, void* userData);
void wrenSetUserData(WrenVM* vm, void* userData);
#endif

View File

@ -36,7 +36,7 @@
// see https://bullno1.com/blog/switched-goto for alternative
// Defaults to true on supported compilers.
#ifndef WREN_COMPUTED_GOTO
#if defined(_MSC_VER) && !defined(__clang__)
#ifdef _MSC_VER
// No computed gotos in Visual Studio.
#define WREN_COMPUTED_GOTO 0
#else

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,6 @@
#include "wren_common.h"
#include "wren_core.h"
#include "wren_math.h"
#include "wren_primitive.h"
#include "wren_value.h"
@ -50,11 +49,6 @@ DEF_PRIMITIVE(class_toString)
RETURN_OBJ(AS_CLASS(args[0])->name);
}
DEF_PRIMITIVE(class_attributes)
{
RETURN_VAL(AS_CLASS(args[0])->attributes);
}
DEF_PRIMITIVE(fiber_new)
{
if (!validateFn(vm, args[1], "Argument")) return false;
@ -197,15 +191,6 @@ DEF_PRIMITIVE(fiber_try)
return false;
}
DEF_PRIMITIVE(fiber_try1)
{
runFiber(vm, AS_FIBER(args[0]), args, true, true, "try");
// If we're switching to a valid fiber to try, remember that we're trying it.
if (!wrenHasError(vm->fiber)) vm->fiber->state = FIBER_TRY;
return false;
}
DEF_PRIMITIVE(fiber_yield)
{
ObjFiber* current = vm->fiber;
@ -263,6 +248,13 @@ DEF_PRIMITIVE(fn_arity)
static void call_fn(WrenVM* vm, Value* args, int numArgs)
{
// We only care about missing arguments, not extras.
if (AS_CLOSURE(args[0])->fn->arity > numArgs)
{
vm->fiber->error = CONST_STRING(vm, "Function expects more arguments.");
return;
}
// +1 to include the function itself.
wrenCallFunction(vm, vm->fiber, AS_CLOSURE(args[0]), numArgs + 1);
}
@ -399,34 +391,6 @@ DEF_PRIMITIVE(list_removeAt)
RETURN_VAL(wrenListRemoveAt(vm, list, index));
}
DEF_PRIMITIVE(list_removeValue) {
ObjList* list = AS_LIST(args[0]);
int index = wrenListIndexOf(vm, list, args[1]);
if(index == -1) RETURN_NULL;
RETURN_VAL(wrenListRemoveAt(vm, list, index));
}
DEF_PRIMITIVE(list_indexOf)
{
ObjList* list = AS_LIST(args[0]);
RETURN_NUM(wrenListIndexOf(vm, list, args[1]));
}
DEF_PRIMITIVE(list_swap)
{
ObjList* list = AS_LIST(args[0]);
uint32_t indexA = validateIndex(vm, args[1], list->elements.count, "Index 0");
if (indexA == UINT32_MAX) return false;
uint32_t indexB = validateIndex(vm, args[2], list->elements.count, "Index 1");
if (indexB == UINT32_MAX) return false;
Value a = list->elements.data[indexA];
list->elements.data[indexA] = list->elements.data[indexB];
list->elements.data[indexB] = a;
RETURN_NULL;
}
DEF_PRIMITIVE(list_subscript)
{
ObjList* list = AS_LIST(args[0]);
@ -630,23 +594,10 @@ DEF_PRIMITIVE(num_fromString)
RETURN_NUM(number);
}
// Defines a primitive on Num that calls infix [op] and returns [type].
#define DEF_NUM_CONSTANT(name, value) \
DEF_PRIMITIVE(num_##name) \
{ \
RETURN_NUM(value); \
}
DEF_NUM_CONSTANT(infinity, INFINITY)
DEF_NUM_CONSTANT(nan, WREN_DOUBLE_NAN)
DEF_NUM_CONSTANT(pi, 3.14159265358979323846264338327950288)
DEF_NUM_CONSTANT(tau, 6.28318530717958647692528676655900577)
DEF_NUM_CONSTANT(largest, DBL_MAX)
DEF_NUM_CONSTANT(smallest, DBL_MIN)
DEF_NUM_CONSTANT(maxSafeInteger, 9007199254740991.0)
DEF_NUM_CONSTANT(minSafeInteger, -9007199254740991.0)
DEF_PRIMITIVE(num_pi)
{
RETURN_NUM(3.14159265358979323846);
}
// Defines a primitive on Num that calls infix [op] and returns [type].
#define DEF_NUM_INFIX(name, op, type) \
@ -692,7 +643,6 @@ DEF_NUM_FN(abs, fabs)
DEF_NUM_FN(acos, acos)
DEF_NUM_FN(asin, asin)
DEF_NUM_FN(atan, atan)
DEF_NUM_FN(cbrt, cbrt)
DEF_NUM_FN(ceil, ceil)
DEF_NUM_FN(cos, cos)
DEF_NUM_FN(floor, floor)
@ -749,52 +699,18 @@ DEF_PRIMITIVE(num_dotDotDot)
DEF_PRIMITIVE(num_atan2)
{
if (!validateNum(vm, args[1], "x value")) return false;
RETURN_NUM(atan2(AS_NUM(args[0]), AS_NUM(args[1])));
}
DEF_PRIMITIVE(num_min)
{
if (!validateNum(vm, args[1], "Other value")) return false;
double value = AS_NUM(args[0]);
double other = AS_NUM(args[1]);
RETURN_NUM(value <= other ? value : other);
}
DEF_PRIMITIVE(num_max)
{
if (!validateNum(vm, args[1], "Other value")) return false;
double value = AS_NUM(args[0]);
double other = AS_NUM(args[1]);
RETURN_NUM(value > other ? value : other);
}
DEF_PRIMITIVE(num_clamp)
{
if (!validateNum(vm, args[1], "Min value")) return false;
if (!validateNum(vm, args[2], "Max value")) return false;
double value = AS_NUM(args[0]);
double min = AS_NUM(args[1]);
double max = AS_NUM(args[2]);
double result = (value < min) ? min : ((value > max) ? max : value);
RETURN_NUM(result);
}
DEF_PRIMITIVE(num_pow)
{
if (!validateNum(vm, args[1], "Power value")) return false;
RETURN_NUM(pow(AS_NUM(args[0]), AS_NUM(args[1])));
}
DEF_PRIMITIVE(num_fraction)
{
double unused;
RETURN_NUM(modf(AS_NUM(args[0]) , &unused));
double dummy;
RETURN_NUM(modf(AS_NUM(args[0]) , &dummy));
}
DEF_PRIMITIVE(num_isInfinity)
@ -831,6 +747,16 @@ DEF_PRIMITIVE(num_sign)
}
}
DEF_PRIMITIVE(num_largest)
{
RETURN_NUM(DBL_MAX);
}
DEF_PRIMITIVE(num_smallest)
{
RETURN_NUM(DBL_MIN);
}
DEF_PRIMITIVE(num_toString)
{
RETURN_VAL(wrenNumToString(vm, AS_NUM(args[0])));
@ -1257,7 +1183,6 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->classClass, "name", class_name);
PRIMITIVE(vm->classClass, "supertype", class_supertype);
PRIMITIVE(vm->classClass, "toString", class_toString);
PRIMITIVE(vm->classClass, "attributes", class_attributes);
// Finally, we can define Object's metaclass which is a subclass of Class.
ObjClass* objectMetaclass = defineClass(vm, coreModule, "Object metaclass");
@ -1317,31 +1242,28 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->fiberClass, "transfer(_)", fiber_transfer1);
PRIMITIVE(vm->fiberClass, "transferError(_)", fiber_transferError);
PRIMITIVE(vm->fiberClass, "try()", fiber_try);
PRIMITIVE(vm->fiberClass, "try(_)", fiber_try1);
vm->fnClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Fn"));
PRIMITIVE(vm->fnClass->obj.classObj, "new(_)", fn_new);
PRIMITIVE(vm->fnClass, "arity", fn_arity);
FUNCTION_CALL(vm->fnClass, "call()", fn_call0);
FUNCTION_CALL(vm->fnClass, "call(_)", fn_call1);
FUNCTION_CALL(vm->fnClass, "call(_,_)", fn_call2);
FUNCTION_CALL(vm->fnClass, "call(_,_,_)", fn_call3);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_)", fn_call4);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_)", fn_call5);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_)", fn_call6);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_,_)", fn_call7);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_,_,_)", fn_call8);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_,_,_,_)", fn_call9);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_)", fn_call10);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_)", fn_call11);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_)", fn_call12);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call13);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call14);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call15);
FUNCTION_CALL(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call16);
PRIMITIVE(vm->fnClass, "call()", fn_call0);
PRIMITIVE(vm->fnClass, "call(_)", fn_call1);
PRIMITIVE(vm->fnClass, "call(_,_)", fn_call2);
PRIMITIVE(vm->fnClass, "call(_,_,_)", fn_call3);
PRIMITIVE(vm->fnClass, "call(_,_,_,_)", fn_call4);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_)", fn_call5);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_)", fn_call6);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_)", fn_call7);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_)", fn_call8);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_)", fn_call9);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_)", fn_call10);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_)", fn_call11);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_)", fn_call12);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call13);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call14);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call15);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call16);
PRIMITIVE(vm->fnClass, "toString", fn_toString);
vm->nullClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Null"));
@ -1350,14 +1272,9 @@ void wrenInitializeCore(WrenVM* vm)
vm->numClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Num"));
PRIMITIVE(vm->numClass->obj.classObj, "fromString(_)", num_fromString);
PRIMITIVE(vm->numClass->obj.classObj, "infinity", num_infinity);
PRIMITIVE(vm->numClass->obj.classObj, "nan", num_nan);
PRIMITIVE(vm->numClass->obj.classObj, "pi", num_pi);
PRIMITIVE(vm->numClass->obj.classObj, "tau", num_tau);
PRIMITIVE(vm->numClass->obj.classObj, "largest", num_largest);
PRIMITIVE(vm->numClass->obj.classObj, "smallest", num_smallest);
PRIMITIVE(vm->numClass->obj.classObj, "maxSafeInteger", num_maxSafeInteger);
PRIMITIVE(vm->numClass->obj.classObj, "minSafeInteger", num_minSafeInteger);
PRIMITIVE(vm->numClass, "-(_)", num_minus);
PRIMITIVE(vm->numClass, "+(_)", num_plus);
PRIMITIVE(vm->numClass, "*(_)", num_multiply);
@ -1375,15 +1292,11 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->numClass, "acos", num_acos);
PRIMITIVE(vm->numClass, "asin", num_asin);
PRIMITIVE(vm->numClass, "atan", num_atan);
PRIMITIVE(vm->numClass, "cbrt", num_cbrt);
PRIMITIVE(vm->numClass, "ceil", num_ceil);
PRIMITIVE(vm->numClass, "cos", num_cos);
PRIMITIVE(vm->numClass, "floor", num_floor);
PRIMITIVE(vm->numClass, "-", num_negate);
PRIMITIVE(vm->numClass, "round", num_round);
PRIMITIVE(vm->numClass, "min(_)", num_min);
PRIMITIVE(vm->numClass, "max(_)", num_max);
PRIMITIVE(vm->numClass, "clamp(_,_)", num_clamp);
PRIMITIVE(vm->numClass, "sin", num_sin);
PRIMITIVE(vm->numClass, "sqrt", num_sqrt);
PRIMITIVE(vm->numClass, "tan", num_tan);
@ -1440,9 +1353,6 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->listClass, "iterate(_)", list_iterate);
PRIMITIVE(vm->listClass, "iteratorValue(_)", list_iteratorValue);
PRIMITIVE(vm->listClass, "removeAt(_)", list_removeAt);
PRIMITIVE(vm->listClass, "remove(_)", list_removeValue);
PRIMITIVE(vm->listClass, "indexOf(_)", list_indexOf);
PRIMITIVE(vm->listClass, "swap(_,_)", list_swap);
vm->mapClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Map"));
PRIMITIVE(vm->mapClass->obj.classObj, "new()", map_new);

View File

@ -323,41 +323,6 @@ class List is Sequence {
return other
}
sort() { sort {|low, high| low < high } }
sort(comparer) {
if (!(comparer is Fn)) {
Fiber.abort("Comparer must be a function.")
}
quicksort_(0, count - 1, comparer)
return this
}
quicksort_(low, high, comparer) {
if (low < high) {
var p = partition_(low, high, comparer)
quicksort_(low, p - 1, comparer)
quicksort_(p + 1, high, comparer)
}
}
partition_(low, high, comparer) {
var p = this[high]
var i = low - 1
for (j in low..(high-1)) {
if (comparer.call(this[j], p)) {
i = i + 1
var t = this[i]
this[i] = this[j]
this[j] = t
}
}
var t = this[i+1]
this[i+1] = this[high]
this[high] = t
return i+1
}
toString { "[%(join(", "))]" }
+(other) {
@ -471,13 +436,3 @@ class System {
}
}
}
class ClassAttributes {
self { _attributes }
methods { _methods }
construct new(attributes, methods) {
_attributes = attributes
_methods = methods
}
toString { "attributes:%(_attributes) methods:%(_methods)" }
}

View File

@ -325,41 +325,6 @@ static const char* coreModuleSource =
" return other\n"
" }\n"
"\n"
" sort() { sort {|low, high| low < high } }\n"
"\n"
" sort(comparer) {\n"
" if (!(comparer is Fn)) {\n"
" Fiber.abort(\"Comparer must be a function.\")\n"
" }\n"
" quicksort_(0, count - 1, comparer)\n"
" return this\n"
" }\n"
"\n"
" quicksort_(low, high, comparer) {\n"
" if (low < high) {\n"
" var p = partition_(low, high, comparer)\n"
" quicksort_(low, p - 1, comparer)\n"
" quicksort_(p + 1, high, comparer)\n"
" }\n"
" }\n"
"\n"
" partition_(low, high, comparer) {\n"
" var p = this[high]\n"
" var i = low - 1\n"
" for (j in low..(high-1)) {\n"
" if (comparer.call(this[j], p)) { \n"
" i = i + 1\n"
" var t = this[i]\n"
" this[i] = this[j]\n"
" this[j] = t\n"
" }\n"
" }\n"
" var t = this[i+1]\n"
" this[i+1] = this[high]\n"
" this[high] = t\n"
" return i+1\n"
" }\n"
"\n"
" toString { \"[%(join(\", \"))]\" }\n"
"\n"
" +(other) {\n"
@ -472,15 +437,4 @@ static const char* coreModuleSource =
" writeString_(\"[invalid toString]\")\n"
" }\n"
" }\n"
"}\n"
"\n"
"class ClassAttributes {\n"
" self { _attributes }\n"
" methods { _methods }\n"
" construct new(attributes, methods) {\n"
" _attributes = attributes\n"
" _methods = methods\n"
" }\n"
" toString { \"attributes:%(_attributes) methods:%(_methods)\" }\n"
"}\n";

View File

@ -296,7 +296,6 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
}
case CODE_FOREIGN_CLASS: printf("FOREIGN_CLASS\n"); break;
case CODE_END_CLASS: printf("END_CLASS\n"); break;
case CODE_METHOD_INSTANCE:
{

View File

@ -1,34 +0,0 @@
#ifndef wren_math_h
#define wren_math_h
#include <math.h>
#include <stdint.h>
// A union to let us reinterpret a double as raw bits and back.
typedef union
{
uint64_t bits64;
uint32_t bits32[2];
double num;
} WrenDoubleBits;
#define WREN_DOUBLE_QNAN_POS_MIN_BITS (UINT64_C(0x7FF8000000000000))
#define WREN_DOUBLE_QNAN_POS_MAX_BITS (UINT64_C(0x7FFFFFFFFFFFFFFF))
#define WREN_DOUBLE_NAN (wrenDoubleFromBits(WREN_DOUBLE_QNAN_POS_MIN_BITS))
static inline double wrenDoubleFromBits(uint64_t bits)
{
WrenDoubleBits data;
data.bits64 = bits;
return data.num;
}
static inline uint64_t wrenDoubleToBits(double num)
{
WrenDoubleBits data;
data.num = num;
return data.bits64;
}
#endif

View File

@ -169,10 +169,6 @@ OPCODE(FOREIGN_CONSTRUCT, 0)
// the name of the class. Byte [arg] is the number of fields in the class.
OPCODE(CLASS, -1)
// Ends a class.
// Atm the stack contains the class and the ClassAttributes (or null).
OPCODE(END_CLASS, -2)
// Creates a foreign class. Top of stack is the superclass. Below that is a
// string for the name of the class.
OPCODE(FOREIGN_CLASS, -1)

View File

@ -47,7 +47,11 @@ bool validateInt(WrenVM* vm, Value arg, const char* argName)
bool validateKey(WrenVM* vm, Value arg)
{
if (wrenMapIsValidKey(arg)) return true;
if (IS_BOOL(arg) || IS_CLASS(arg) || IS_NULL(arg) ||
IS_NUM(arg) || IS_RANGE(arg) || IS_STRING(arg))
{
return true;
}
RETURN_ERROR("Key must be a value type.");
}

View File

@ -16,19 +16,6 @@
wrenBindMethod(vm, cls, symbol, method); \
} while (false)
// Binds a primitive method named [name] (in Wren) implemented using C function
// [fn] to `ObjClass` [cls], but as a FN call.
#define FUNCTION_CALL(cls, name, function) \
do \
{ \
int symbol = wrenSymbolTableEnsure(vm, \
&vm->methodNames, name, strlen(name)); \
Method method; \
method.type = METHOD_FUNCTION_CALL; \
method.as.primitive = prim_##function; \
wrenBindMethod(vm, cls, symbol, method); \
} while (false)
// Defines a primitive method whose C function name is [name]. This abstracts
// the actual type signature of a primitive function and makes it clear which C
// functions are invoked as primitives.
@ -81,7 +68,7 @@ bool validateInt(WrenVM* vm, Value arg, const char* argName);
// Validates that [arg] is a valid object for use as a map key. Returns true if
// it is. If not, reports an error and returns false.
bool validateKey(WrenVM * vm, Value arg);
bool validateKey(WrenVM* vm, Value arg);
// Validates that the argument at [argIndex] is an integer within `[0, count)`.
// Also allows negative indices which map backwards from the end. Returns the

View File

@ -194,14 +194,3 @@ int wrenPowerOf2Ceil(int n)
return n;
}
uint32_t wrenValidateIndex(uint32_t count, int64_t value)
{
// Negative indices count from the end.
if (value < 0) value = count + value;
// Check bounds.
if (value >= 0 && value < count) return (uint32_t)value;
return UINT32_MAX;
}

View File

@ -118,9 +118,4 @@ int wrenUtf8DecodeNumBytes(uint8_t byte);
// Returns the smallest power of two that is equal to or greater than [n].
int wrenPowerOf2Ceil(int n);
// Validates that [value] is within `[0, count)`. Also allows
// negative indices which map backwards from the end. Returns the valid positive
// index value. If invalid, returns `UINT32_MAX`.
uint32_t wrenValidateIndex(uint32_t count, int64_t value);
#endif

View File

@ -50,7 +50,6 @@ ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name)
classObj->superclass = NULL;
classObj->numFields = numFields;
classObj->name = name;
classObj->attributes = NULL_VAL;
wrenPushRoot(vm, (Obj*)classObj);
wrenMethodBufferInit(&classObj->methods);
@ -320,19 +319,6 @@ void wrenListInsert(WrenVM* vm, ObjList* list, Value value, uint32_t index)
list->elements.data[index] = value;
}
int wrenListIndexOf(WrenVM* vm, ObjList* list, Value value)
{
int count = list->elements.count;
for (int i = 0; i < count; i++)
{
Value item = list->elements.data[i];
if(wrenValuesEqual(item, value)) {
return i;
}
}
return -1;
}
Value wrenListRemoveAt(WrenVM* vm, ObjList* list, uint32_t index)
{
Value removed = list->elements.data[index];
@ -388,7 +374,9 @@ static inline uint32_t hashBits(uint64_t hash)
static inline uint32_t hashNumber(double num)
{
// Hash the raw bits of the value.
return hashBits(wrenDoubleToBits(num));
DoubleBits bits;
bits.num = num;
return hashBits(bits.bits64);
}
// Generates a hash code for [object].
@ -989,8 +977,7 @@ void wrenGrayObj(WrenVM* vm, Obj* obj)
{
vm->grayCapacity = vm->grayCount * 2;
vm->gray = (Obj**)vm->config.reallocateFn(vm->gray,
vm->grayCapacity * sizeof(Obj*),
vm->config.userData);
vm->grayCapacity * sizeof(Obj*));
}
vm->gray[vm->grayCount++] = obj;
@ -1029,8 +1016,6 @@ static void blackenClass(WrenVM* vm, ObjClass* classObj)
wrenGrayObj(vm, (Obj*)classObj->name);
if(!IS_NULL(classObj->attributes)) wrenGrayObj(vm, AS_OBJ(classObj->attributes));
// Keep track of how much memory is still in use.
vm->bytesAllocated += sizeof(ObjClass);
vm->bytesAllocated += classObj->methods.capacity * sizeof(Method);

View File

@ -5,7 +5,6 @@
#include <string.h>
#include "wren_common.h"
#include "wren_math.h"
#include "wren_utils.h"
// This defines the built-in types and their core representations in memory.
@ -196,7 +195,7 @@ typedef struct sObjUpvalue
// The type of a primitive function.
//
// Primitives are similar to foreign functions, but have more direct access to
// Primitives are similiar to foreign functions, but have more direct access to
// VM internals. It is passed the arguments in [args]. If it returns a value,
// it places it in `args[0]` and returns `true`. If it causes a runtime error
// or modifies the running fiber, it returns `false`.
@ -360,9 +359,6 @@ typedef enum
// this can directly manipulate the fiber's stack.
METHOD_PRIMITIVE,
// A primitive that handles .call on Fn.
METHOD_FUNCTION_CALL,
// A externally-defined C method.
METHOD_FOREIGN,
@ -410,9 +406,6 @@ struct sObjClass
// The name of the class.
ObjString* name;
// The ClassAttribute for the class, if any
Value attributes;
};
typedef struct
@ -617,6 +610,14 @@ typedef struct
#endif
// A union to let us reinterpret a double as raw bits and back.
typedef union
{
uint64_t bits64;
uint32_t bits32[2];
double num;
} DoubleBits;
// Creates a new "raw" class. It has no metaclass or superclass whatsoever.
// This is only used for bootstrapping the initial Object and Class classes,
// which are a little special.
@ -683,17 +684,9 @@ void wrenListInsert(WrenVM* vm, ObjList* list, Value value, uint32_t index);
// Removes and returns the item at [index] from [list].
Value wrenListRemoveAt(WrenVM* vm, ObjList* list, uint32_t index);
// Searches for [value] in [list], returns the index or -1 if not found.
int wrenListIndexOf(WrenVM* vm, ObjList* list, Value value);
// Creates a new empty map.
ObjMap* wrenNewMap(WrenVM* vm);
// Validates that [arg] is a valid object for use as a map key. Returns true if
// it is and returns false otherwise. Use validateKey usually, for a runtime error.
// This separation exists to aid the API in surfacing errors to the developer as well.
static inline bool wrenMapIsValidKey(Value arg);
// Looks up [key] in [map]. If found, returns the value. Otherwise, returns
// `UNDEFINED_VAL`.
Value wrenMapGet(ObjMap* map, Value key);
@ -858,7 +851,9 @@ static inline Value wrenObjectToValue(Obj* obj)
static inline double wrenValueToNum(Value value)
{
#if WREN_NAN_TAGGING
return wrenDoubleFromBits(value);
DoubleBits data;
data.bits64 = value;
return data.num;
#else
return value.as.num;
#endif
@ -868,7 +863,9 @@ static inline double wrenValueToNum(Value value)
static inline Value wrenNumToValue(double num)
{
#if WREN_NAN_TAGGING
return wrenDoubleToBits(num);
DoubleBits data;
data.num = num;
return data.bits64;
#else
Value value;
value.type = VAL_NUM;
@ -877,14 +874,4 @@ static inline Value wrenNumToValue(double num)
#endif
}
static inline bool wrenMapIsValidKey(Value arg)
{
return IS_BOOL(arg)
|| IS_CLASS(arg)
|| IS_NULL(arg)
|| IS_NUM(arg)
|| IS_RANGE(arg)
|| IS_STRING(arg);
}
#endif

View File

@ -25,7 +25,7 @@
// may return a non-NULL pointer which must not be dereferenced but nevertheless
// should be freed. To prevent that, we avoid calling realloc() with a zero
// size.
static void* defaultReallocate(void* ptr, size_t newSize, void* _)
static void* defaultReallocate(void* ptr, size_t newSize)
{
if (newSize == 0)
{
@ -36,11 +36,6 @@ static void* defaultReallocate(void* ptr, size_t newSize, void* _)
return realloc(ptr, newSize);
}
int wrenGetVersionNumber()
{
return WREN_VERSION_NUMBER;
}
void wrenInitConfiguration(WrenConfiguration* config)
{
config->reallocateFn = defaultReallocate;
@ -59,23 +54,15 @@ void wrenInitConfiguration(WrenConfiguration* config)
WrenVM* wrenNewVM(WrenConfiguration* config)
{
WrenReallocateFn reallocate = defaultReallocate;
void* userData = NULL;
if (config != NULL) {
userData = config->userData;
reallocate = config->reallocateFn ? config->reallocateFn : defaultReallocate;
}
if (config != NULL) reallocate = config->reallocateFn;
WrenVM* vm = (WrenVM*)reallocate(NULL, sizeof(*vm), userData);
WrenVM* vm = (WrenVM*)reallocate(NULL, sizeof(*vm));
memset(vm, 0, sizeof(WrenVM));
// Copy the configuration if given one.
if (config != NULL)
{
memcpy(&vm->config, config, sizeof(WrenConfiguration));
// We choose to set this after copying,
// rather than modifying the user config pointer
vm->config.reallocateFn = reallocate;
}
else
{
@ -86,7 +73,7 @@ WrenVM* wrenNewVM(WrenConfiguration* config)
vm->grayCount = 0;
// TODO: Tune this.
vm->grayCapacity = 4;
vm->gray = (Obj**)reallocate(NULL, vm->grayCapacity * sizeof(Obj*), userData);
vm->gray = (Obj**)reallocate(NULL, vm->grayCapacity * sizeof(Obj*));
vm->nextGC = vm->config.initialHeapSize;
wrenSymbolTableInit(&vm->methodNames);
@ -110,7 +97,7 @@ void wrenFreeVM(WrenVM* vm)
}
// Free up the GC gray set.
vm->gray = (Obj**)vm->config.reallocateFn(vm->gray, 0, vm->config.userData);
vm->gray = (Obj**)vm->config.reallocateFn(vm->gray, 0);
// Tell the user if they didn't free any handles. We don't want to just free
// them here because the host app may still have pointers to them that they
@ -201,12 +188,12 @@ void wrenCollectGarbage(WrenVM* vm)
double elapsed = ((double)clock() / CLOCKS_PER_SEC) - startTime;
// Explicit cast because size_t has different sizes on 32-bit and 64-bit and
// we need a consistent type for the format string.
printf("GC %lu before, %lu after (%lu collected), next at %lu. Took %.3fms.\n",
printf("GC %lu before, %lu after (%lu collected), next at %lu. Took %.3fs.\n",
(unsigned long)before,
(unsigned long)vm->bytesAllocated,
(unsigned long)(before - vm->bytesAllocated),
(unsigned long)vm->nextGC,
elapsed*1000.0);
elapsed);
#endif
}
@ -233,7 +220,7 @@ void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize)
if (newSize > 0 && vm->bytesAllocated > vm->nextGC) wrenCollectGarbage(vm);
#endif
return vm->config.reallocateFn(memory, newSize, vm->config.userData);
return vm->config.reallocateFn(memory, newSize);
}
// Captures the local variable [local] into an [Upvalue]. If that local is
@ -525,10 +512,7 @@ static Value validateSuperclass(WrenVM* vm, Value name, Value superclassValue,
superclass == vm->listClass ||
superclass == vm->mapClass ||
superclass == vm->rangeClass ||
superclass == vm->stringClass ||
superclass == vm->boolClass ||
superclass == vm->nullClass ||
superclass == vm->numClass)
superclass == vm->stringClass)
{
return wrenStringFormat(vm,
"Class '@' cannot inherit from built-in class '@'.",
@ -607,27 +591,6 @@ static void bindForeignClass(WrenVM* vm, ObjClass* classObj, ObjModule* module)
}
}
// Completes the process for creating a new class.
//
// The class attributes instance and the class itself should be on the
// top of the fiber's stack.
//
// This process handles moving the attribute data for a class from
// compile time to runtime, since it now has all the attributes associated
// with a class, including for methods.
static void endClass(WrenVM* vm)
{
// Pull the attributes and class off the stack
Value attributes = vm->fiber->stackTop[-2];
Value classValue = vm->fiber->stackTop[-1];
// Remove the stack items
vm->fiber->stackTop -= 2;
ObjClass* classObj = AS_CLASS(classValue);
classObj->attributes = attributes;
}
// Creates a new class.
//
// If [numFields] is -1, the class is a foreign class. The name and superclass
@ -806,21 +769,6 @@ static Value getModuleVariable(WrenVM* vm, ObjModule* module,
return NULL_VAL;
}
inline static bool checkArity(WrenVM* vm, Value value, int numArgs)
{
ASSERT(IS_CLOSURE(value), "Receiver must be a closure.");
ObjFn* fn = AS_CLOSURE(value)->fn;
// We only care about missing arguments, not extras. The "- 1" is because
// numArgs includes the receiver, the function itself, which we don't want to
// count.
if (numArgs - 1 >= fn->arity) return true;
vm->fiber->error = CONST_STRING(vm, "Function expects more arguments.");
return false;
}
// The main bytecode interpreter loop. This is where the magic happens. It is
// also, as you can imagine, highly performance critical.
static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
@ -1062,17 +1010,6 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
}
break;
case METHOD_FUNCTION_CALL:
if (!checkArity(vm, args[0], numArgs)) {
RUNTIME_ERROR();
break;
}
STORE_FRAME();
method->as.primitive(vm, args);
LOAD_FRAME();
break;
case METHOD_FOREIGN:
callForeign(vm, fiber, method->as.foreign, numArgs);
if (wrenHasError(fiber)) RUNTIME_ERROR();
@ -1166,7 +1103,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
uint16_t offset = READ_SHORT();
Value condition = POP();
if (wrenIsFalsyValue(condition)) ip += offset;
if (IS_FALSE(condition) || IS_NULL(condition)) ip += offset;
DISPATCH();
}
@ -1175,7 +1112,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
uint16_t offset = READ_SHORT();
Value condition = PEEK();
if (wrenIsFalsyValue(condition))
if (IS_FALSE(condition) || IS_NULL(condition))
{
// Short-circuit the right hand side.
ip += offset;
@ -1193,7 +1130,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
uint16_t offset = READ_SHORT();
Value condition = PEEK();
if (wrenIsFalsyValue(condition))
if (IS_FALSE(condition) || IS_NULL(condition))
{
// Discard the condition and evaluate the right hand side.
DROP();
@ -1295,13 +1232,6 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
DISPATCH();
}
CASE_CODE(END_CLASS):
{
endClass(vm);
if (wrenHasError(fiber)) RUNTIME_ERROR();
DISPATCH();
}
CASE_CODE(CLASS):
{
createClass(vm, READ_BYTE(), NULL);
@ -1519,9 +1449,12 @@ WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
wrenPushRoot(vm, (Obj*)closure);
ObjFiber* fiber = wrenNewFiber(vm, closure);
wrenPopRoot(vm); // closure.
vm->apiStack = NULL;
return runInterpreter(vm, fiber);
WrenInterpretResult result = runInterpreter(vm, fiber);
vm->fiber = NULL;
vm->apiStack = NULL;
return result;
}
ObjClosure* wrenCompileSource(WrenVM* vm, const char* module, const char* source,
@ -1803,27 +1736,9 @@ void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot)
validateApiSlot(vm, listSlot);
validateApiSlot(vm, elementSlot);
ASSERT(IS_LIST(vm->apiStack[listSlot]), "Slot must hold a list.");
ValueBuffer elements = AS_LIST(vm->apiStack[listSlot])->elements;
uint32_t usedIndex = wrenValidateIndex(elements.count, index);
ASSERT(usedIndex != UINT32_MAX, "Index out of bounds.");
vm->apiStack[elementSlot] = elements.data[usedIndex];
}
void wrenSetListElement(WrenVM* vm, int listSlot, int index, int elementSlot)
{
validateApiSlot(vm, listSlot);
validateApiSlot(vm, elementSlot);
ASSERT(IS_LIST(vm->apiStack[listSlot]), "Slot must hold a list.");
ObjList* list = AS_LIST(vm->apiStack[listSlot]);
uint32_t usedIndex = wrenValidateIndex(list->elements.count, index);
ASSERT(usedIndex != UINT32_MAX, "Index out of bounds.");
list->elements.data[usedIndex] = vm->apiStack[elementSlot];
ValueBuffer elements = AS_LIST(vm->apiStack[listSlot])->elements;
vm->apiStack[elementSlot] = elements.data[index];
}
void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
@ -1834,8 +1749,7 @@ void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
ObjList* list = AS_LIST(vm->apiStack[listSlot]);
// Negative indices count from the end.
// We don't use wrenValidateIndex here because insert allows 1 past the end.
// Negative indices count from the end.
if (index < 0) index = list->elements.count + 1 + index;
ASSERT(index <= list->elements.count, "Index out of bounds.");
@ -1859,7 +1773,6 @@ bool wrenGetMapContainsKey(WrenVM* vm, int mapSlot, int keySlot)
ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Slot must hold a map.");
Value key = vm->apiStack[keySlot];
ASSERT(wrenMapIsValidKey(key), "Key must be a value type");
if (!validateKey(vm, key)) return false;
ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
@ -1892,8 +1805,6 @@ void wrenSetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot)
ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Must insert into a map.");
Value key = vm->apiStack[keySlot];
ASSERT(wrenMapIsValidKey(key), "Key must be a value type");
if (!validateKey(vm, key)) {
return;
}
@ -1942,40 +1853,6 @@ void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
setSlot(vm, slot, moduleObj->variables.data[variableSlot]);
}
bool wrenHasVariable(WrenVM* vm, const char* module, const char* name)
{
ASSERT(module != NULL, "Module cannot be NULL.");
ASSERT(name != NULL, "Variable name cannot be NULL.");
Value moduleName = wrenStringFormat(vm, "$", module);
wrenPushRoot(vm, AS_OBJ(moduleName));
//We don't use wrenHasModule since we want to use the module object.
ObjModule* moduleObj = getModule(vm, moduleName);
ASSERT(moduleObj != NULL, "Could not find module.");
wrenPopRoot(vm); // moduleName.
int variableSlot = wrenSymbolTableFind(&moduleObj->variableNames,
name, strlen(name));
return variableSlot != -1;
}
bool wrenHasModule(WrenVM* vm, const char* module)
{
ASSERT(module != NULL, "Module cannot be NULL.");
Value moduleName = wrenStringFormat(vm, "$", module);
wrenPushRoot(vm, AS_OBJ(moduleName));
ObjModule* moduleObj = getModule(vm, moduleName);
wrenPopRoot(vm); // moduleName.
return moduleObj != NULL;
}
void wrenAbortFiber(WrenVM* vm, int slot)
{
validateApiSlot(vm, slot);

View File

@ -243,9 +243,4 @@ static inline bool wrenIsLocalName(const char* name)
return name[0] >= 'a' && name[0] <= 'z';
}
static inline bool wrenIsFalsyValue(Value value)
{
return IS_FALSE(value) || IS_NULL(value);
}
#endif

View File

@ -2,7 +2,6 @@
#include <string.h>
#include "wren.h"
#include "../test.h"
int callWrenCallRootRunTests(WrenVM* vm)
{
@ -18,7 +17,7 @@ int callWrenCallRootRunTests(WrenVM* vm)
WrenInterpretResult result = wrenCall(vm, run);
if (result == WREN_RESULT_RUNTIME_ERROR)
{
exitCode = WREN_EX_SOFTWARE;
exitCode = 70;
}
else
{

View File

@ -32,25 +32,6 @@ static void otherModule(WrenVM* vm)
wrenGetVariable(vm, "./test/api/get_variable_module", "Variable", 0);
}
static void hasVariable(WrenVM* vm)
{
const char* module = wrenGetSlotString(vm, 1);
const char* variable = wrenGetSlotString(vm, 2);
bool result = wrenHasVariable(vm, module, variable);
wrenEnsureSlots(vm, 1);
wrenSetSlotBool(vm, 0, result);
}
static void hasModule(WrenVM* vm)
{
const char* module = wrenGetSlotString(vm, 1);
bool result = wrenHasModule(vm, module);
wrenEnsureSlots(vm, 1);
wrenSetSlotBool(vm, 0, result);
}
WrenForeignMethodFn getVariableBindMethod(const char* signature)
{
if (strcmp(signature, "static GetVariable.beforeDefined()") == 0) return beforeDefined;
@ -58,9 +39,6 @@ WrenForeignMethodFn getVariableBindMethod(const char* signature)
if (strcmp(signature, "static GetVariable.afterAssigned()") == 0) return afterAssigned;
if (strcmp(signature, "static GetVariable.otherSlot()") == 0) return otherSlot;
if (strcmp(signature, "static GetVariable.otherModule()") == 0) return otherModule;
if (strcmp(signature, "static Has.variable(_,_)") == 0) return hasVariable;
if (strcmp(signature, "static Has.module(_)") == 0) return hasModule;
return NULL;
}

View File

@ -8,11 +8,6 @@ class GetVariable {
foreign static otherModule()
}
class Has {
foreign static variable(module, variable)
foreign static module(module)
}
System.print(GetVariable.beforeDefined()) // expect: null
var A = "a"
@ -27,13 +22,3 @@ var B = "b"
System.print(GetVariable.otherSlot()) // expect: b
System.print(GetVariable.otherModule()) // expect: value
System.print(Has.variable("./test/api/get_variable_module", "Variable")) // expect: true
System.print(Has.variable("./test/api/get_variable_module", "NotAVariable")) // expect: false
System.print(Has.variable("./test/api/get_variable", "Has")) // expect: true
System.print(Has.variable("./test/api/get_variable", "Fake")) // expect: false
System.print(Has.module("./test/api/get_variable_module")) // expect: true
System.print(Has.module("./test/api/get_variable")) // expect: true
System.print(Has.module("not a module")) // expect: false

View File

@ -15,14 +15,6 @@ static void insertNumber(WrenVM* vm, int index, double value)
wrenInsertInList(vm, 0, index, 1);
}
// Helper function to append a double in a slot then insert it into the list at
// slot zero.
static void appendNumber(WrenVM* vm, double value)
{
wrenSetSlotDouble(vm, 1, value);
wrenInsertInList(vm, 0, -1, 1);
}
static void insert(WrenVM* vm)
{
wrenSetSlotNewList(vm, 0);
@ -45,40 +37,10 @@ static void insert(WrenVM* vm)
insertNumber(vm, -3, 9.0);
}
static void get(WrenVM* vm)
{
int listSlot = 1;
int index = (int)wrenGetSlotDouble(vm, 2);
wrenGetListElement(vm, listSlot, index, 0);
}
static void set(WrenVM* vm)
{
wrenSetSlotNewList(vm, 0);
wrenEnsureSlots(vm, 2);
appendNumber(vm, 1.0);
appendNumber(vm, 2.0);
appendNumber(vm, 3.0);
appendNumber(vm, 4.0);
//list[2] = 33
wrenSetSlotDouble(vm, 1, 33);
wrenSetListElement(vm, 0, 2, 1);
//list[-1] = 44
wrenSetSlotDouble(vm, 1, 44);
wrenSetListElement(vm, 0, -1, 1);
}
WrenForeignMethodFn listsBindMethod(const char* signature)
{
if (strcmp(signature, "static Lists.newList()") == 0) return newList;
if (strcmp(signature, "static Lists.insert()") == 0) return insert;
if (strcmp(signature, "static Lists.set()") == 0) return set;
if (strcmp(signature, "static Lists.get(_,_)") == 0) return get;
return NULL;
}

View File

@ -1,8 +1,6 @@
class Lists {
foreign static newList()
foreign static insert()
foreign static set()
foreign static get(list, index)
}
var list = Lists.newList()
@ -10,7 +8,3 @@ System.print(list is List) // expect: true
System.print(list.count) // expect: 0
System.print(Lists.insert()) // expect: [4, 5, 6, 1, 2, 3, 9, 8, 7]
System.print(Lists.set()) // expect: [1, 2, 33, 44]
System.print(Lists.get([1,2,3,4], -2)) // expect: 3
System.print(Lists.get([1,2,3,4], 1)) // expect: 2

View File

@ -49,7 +49,7 @@ static void insert(WrenVM* vm)
wrenSetMapValue(vm, 0, 1, 2);
}
static void removeKey(WrenVM* vm)
static void remove(WrenVM* vm)
{
wrenEnsureSlots(vm, 3);
@ -104,7 +104,7 @@ WrenForeignMethodFn mapsBindMethod(const char* signature)
{
if (strcmp(signature, "static Maps.newMap()") == 0) return newMap;
if (strcmp(signature, "static Maps.insert()") == 0) return insert;
if (strcmp(signature, "static Maps.remove(_)") == 0) return removeKey;
if (strcmp(signature, "static Maps.remove(_)") == 0) return remove;
if (strcmp(signature, "static Maps.count(_)") == 0) return countWren;
if (strcmp(signature, "static Maps.count()") == 0) return countAPI;
if (strcmp(signature, "static Maps.contains()") == 0) return containsAPI;

View File

@ -62,3 +62,7 @@ System.print(Maps.contains(containsMap, "other")) // expect: false
System.print(Maps.contains()) // expect: true
System.print(Maps.containsFalse()) // expect: false
//
Maps.invalidInsert(ForeignClass.new()) // expect runtime error: Key must be a value type.

View File

@ -23,16 +23,12 @@ static void multipleInterpretCalls(WrenVM* vm)
// Handles should be valid across calls into Wren code.
WrenHandle* absMethod = wrenMakeCallHandle(otherVM, "abs");
result = wrenInterpret(otherVM, "main", "import \"random\" for Random");
correct = correct && (result == WREN_RESULT_SUCCESS);
for (int i = 0; i < 5; i++) {
// Calling `wrenEnsureSlots()` before `wrenInterpret()` should not introduce
// problems later.
wrenEnsureSlots(otherVM, 2);
// Calling a foreign function should succeed.
result = wrenInterpret(otherVM, "main", "Random.new(12345)");
result = wrenInterpret(otherVM, "main", "1 + 2");
correct = correct && (result == WREN_RESULT_SUCCESS);
wrenEnsureSlots(otherVM, 2);

View File

@ -3,7 +3,7 @@
#include "resolution.h"
static void writeFn(WrenVM* vm, const char* text)
static void write(WrenVM* vm, const char* text)
{
printf("%s", text);
}
@ -45,7 +45,7 @@ static WrenLoadModuleResult loadModule(WrenVM* vm, const char* module)
static void runTestVM(WrenVM* vm, WrenConfiguration* configuration,
const char* source)
{
configuration->writeFn = writeFn;
configuration->writeFn = write;
configuration->errorFn = reportError;
configuration->loadModuleFn = loadModule;

View File

@ -5,18 +5,6 @@
static const char* data = "my user data";
static const char* otherData = "other user data";
void* testReallocateFn(void* ptr, size_t newSize, void* userData) {
if (strcmp(userData, data) != 0) return NULL;
if (newSize == 0)
{
free(ptr);
return NULL;
}
return realloc(ptr, newSize);
}
static void test(WrenVM* vm)
{
WrenConfiguration configuration;
@ -29,7 +17,6 @@ static void test(WrenVM* vm)
return;
}
configuration.reallocateFn = testReallocateFn;
configuration.userData = (void*)data;
WrenVM* otherVM = wrenNewVM(&configuration);

View File

@ -29,7 +29,7 @@ min_depth = 4
max_depth = 12
stretch_depth = max_depth + 1
start = time.process_time()
start = time.clock()
print("stretch tree of depth %d check:" % stretch_depth, check_tree(make_tree(0, stretch_depth)))
long_lived_tree = make_tree(0, max_depth)
@ -45,4 +45,4 @@ for depth in range(min_depth, stretch_depth, 2):
iterations //= 4
print("long lived tree of depth %d check:" % max_depth, check_tree(long_lived_tree))
print("elapsed: " + str(time.process_time() - start))
print("elapsed: " + str(time.clock() - start))

View File

@ -625,12 +625,12 @@ planner = None
def delta_blue():
global total
start = time.process_time()
start = time.clock()
for i in range(40):
chain_test(100)
projection_test(100)
print(total)
print("elapsed: " + str(time.process_time() - start))
print("elapsed: " + str(time.clock() - start))
if __name__ == '__main__':

View File

@ -6,7 +6,7 @@ def fib(n):
if n < 2: return n
return fib(n - 1) + fib(n - 2)
start = time.process_time()
start = time.clock()
for i in range(0, 5):
print(fib(28))
print("elapsed: " + str(time.process_time() - start))
print("elapsed: " + str(time.clock() - start))

View File

@ -8,7 +8,7 @@ try:
except NameError:
pass
start = time.process_time()
start = time.clock()
list = []
for i in range(0, 1000000):
list.append(i)
@ -17,4 +17,4 @@ sum = 0
for i in list:
sum += i
print(sum)
print("elapsed: " + str(time.process_time() - start))
print("elapsed: " + str(time.clock() - start))

View File

@ -2,7 +2,7 @@ from __future__ import print_function
import time
start = time.process_time()
start = time.clock()
map = {}
@ -17,4 +17,4 @@ print(sum)
for i in range(1, 2000001):
del map[i]
print("elapsed: " + str(time.process_time() - start))
print("elapsed: " + str(time.clock() - start))

View File

@ -78,7 +78,7 @@ for animal in animals:
for adverb in adverbs:
keys.append(adverb + " " + adjective + " " + animal)
start = time.process_time()
start = time.clock()
map = {}
i = 0
@ -94,4 +94,4 @@ for key in keys:
del map[key]
print(sum)
print("elapsed: " + str(time.process_time() - start))
print("elapsed: " + str(time.clock() - start))

View File

@ -34,7 +34,7 @@ class NthToggle(Toggle):
def main():
start = time.process_time()
start = time.clock()
NUM = 100000
@ -74,7 +74,7 @@ def main():
else:
print("false")
print("elapsed: " + str(time.process_time() - start))
print("elapsed: " + str(time.clock() - start))
main()

View File

@ -1,7 +1,7 @@
from __future__ import print_function
import time
start = time.process_time()
start = time.clock()
count = 0
for i in range(0, 1000000):
@ -32,4 +32,4 @@ for i in range(0, 1000000):
count = count + 1
print(count)
print("elapsed: " + str(time.process_time() - start))
print("elapsed: " + str(time.clock() - start))

View File

@ -1,12 +0,0 @@
var fiber = Fiber.new {|v|
System.print("before")
System.print(v)
true.unknownMethod
System.print("after")
}
System.print(fiber.try("value"))
// expect: before
// expect: value
// expect: Bool does not implement 'unknownMethod'.
System.print("after try") // expect: after try

View File

@ -1,16 +0,0 @@
var fiber = Fiber.new {|v|
System.print("before")
System.print(v)
v = Fiber.yield()
System.print(v)
true.unknownMethod
System.print("after")
}
fiber.try("value1")
// expect: before
// expect: value1
System.print(fiber.try("value2"))
// expect: value2
// expect: Bool does not implement 'unknownMethod'.
System.print("after try") // expect: after try

View File

@ -1,3 +0,0 @@
var f1 = Fn.new {|a, b| a + b } // expect runtime error: Bool does not implement '+(_)'.
f1.call(true, false)

View File

@ -1,8 +0,0 @@
var list = [0, 1, 2, 3, 4]
System.print(list.indexOf(4)) // expect: 4
System.print(list.indexOf(2)) // expect: 2
System.print(list.indexOf(3)) // expect: 3
System.print(list.indexOf(0)) // expect: 0
System.print(list.indexOf(100)) // expect: -1
System.print(list.indexOf(-1)) // expect: -1

View File

@ -1,18 +0,0 @@
var a = [1, 2, 3]
a.remove(2)
System.print(a) // expect: [1, 3]
var b = [1, 2, 3]
b.remove(1)
System.print(b) // expect: [2, 3]
var c = [1, 2, 3]
c.remove(3)
System.print(c) // expect: [1, 2]
// Return the removed value.
System.print([3, 4, 5].remove(4)) // expect: 4
System.print([3, 4, 5].remove(5)) // expect: 5
// Return null when not found
System.print([1, 2, 3].remove(8)) // expect: null

View File

@ -1,9 +0,0 @@
System.print([4, 1, 3, 2].sort()) // expect: [1, 2, 3, 4]
var l = [10, 7, 8, 9, 1, 5]
l.sort{|a, b| a < b }
System.print(l) // expect: [1, 5, 7, 8, 9, 10]
l.sort{|a, b| a > b }
System.print(l) // expect: [10, 9, 8, 7, 5, 1]
[10, 7, 8, 9, 1, 5].sort(3) // expect runtime error: Comparer must be a function.

View File

@ -1,9 +0,0 @@
var list = [0, 1, 2, 3, 4]
list.swap(0, 3)
System.print(list) // expect: [3, 1, 2, 0, 4]
list.swap(-1, 2)
System.print(list) // expect: [3, 1, 4, 0, 2]
list.swap(8, 0) // expect runtime error: Index 0 out of bounds.

View File

@ -1 +0,0 @@
0.atan(false) // expect runtime error: x value must be a number.

View File

@ -1,6 +0,0 @@
System.print(8.cbrt) // expect: 2
System.print(1000000.cbrt) // expect: 100
System.print(1.cbrt) // expect: 1
System.print((-0).cbrt) // expect: -0
System.print(0.cbrt) // expect: 0
System.print(-2.cbrt) // expect: -1.2599210498949

View File

@ -1,7 +0,0 @@
var num = 4
System.print(num.clamp(0, 10)) // expect: 4
System.print(num.clamp(0, 1)) // expect: 1
System.print(2.clamp(0, 1)) // expect: 1
System.print((-1).clamp(0, 1)) // expect: 0
System.print((-1).clamp(-20, 0)) // expect: -1

View File

@ -1 +0,0 @@
1.clamp(0, false) // expect runtime error: Max value must be a number.

View File

@ -1 +0,0 @@
1.clamp(false, 2) // expect runtime error: Min value must be a number.

View File

@ -1 +0,0 @@
System.print(Num.maxSafeInteger) // expect: 9.007199254741e+15

View File

@ -1 +0,0 @@
1.max(false) // expect runtime error: Other value must be a number.

View File

@ -1 +0,0 @@
System.print(Num.minSafeInteger) // expect: -9.007199254741e+15

View File

@ -1,5 +0,0 @@
var num = 4
var num2 = 6
System.print(num.max(num2)) // expect: 6
System.print(num.min(num2)) // expect: 4

View File

@ -1 +0,0 @@
1.min(false) // expect runtime error: Other value must be a number.

View File

@ -1,2 +0,0 @@
1.pow(false) // expect runtime error: Power value must be a number.

View File

@ -1,151 +0,0 @@
class Test {
construct new() {}
test0() {
System.print("test0")
return this
}
test1() {
System.print("test1")
return this
}
test2() {
System.print("test2")
return this
}
[index] {
System.print("testSubscript")
return this
}
}
class Tester {
construct new() {
var test = _test = Test.new()
//test local access
test.
test0(). // expect: test0
test1(). // expect: test1
test2() // expect: test2
test
.test0() // expect: test0
.test1() // expect: test1
.test2() // expect: test2
test
.test0() // expect: test0
.test1(). // expect: test1
test2() // expect: test2
test[0] // expect: testSubscript
.test0() // expect: test0
test[0]. // expect: testSubscript
test0() // expect: test0
//test field access
_test.
test0(). // expect: test0
test1(). // expect: test1
test2() // expect: test2
_test
.test0() // expect: test0
.test1() // expect: test1
.test2() // expect: test2
_test
.test0(). // expect: test0
test1(). // expect: test1
test2() // expect: test2
_test[0] // expect: testSubscript
.test0() // expect: test0
_test[0]. // expect: testSubscript
test0() // expect: test0
}
getter { _test }
method() { _test }
} //Tester
//access via methods/getter
var external = Tester.new()
external.getter.
test0(). // expect: test0
test1(). // expect: test1
test2() // expect: test2
external.getter
.test0() // expect: test0
.test1() // expect: test1
.test2() // expect: test2
external.getter.
test0() // expect: test0
.test1() // expect: test1
.test2() // expect: test2
external.getter[0]. // expect: testSubscript
test0() // expect: test0
external.getter[0] // expect: testSubscript
.test0() // expect: test0
external.method().
test0(). // expect: test0
test1(). // expect: test1
test2() // expect: test2
external.method()
.test0() // expect: test0
.test1() // expect: test1
.test2() // expect: test2
external.method().
test0() // expect: test0
.test1(). // expect: test1
test2() // expect: test2
external.method()[0]. // expect: testSubscript
test0() // expect: test0
external.method()[0] // expect: testSubscript
.test0() // expect: test0
//regular access in module scope
var other = Test.new()
other.
test0(). // expect: test0
test1(). // expect: test1
test2() // expect: test2
other
.test0() // expect: test0
.test1() // expect: test1
.test2() // expect: test2
other
.test0(). // expect: test0
test1() // expect: test1
.test2() // expect: test2
other[0] // expect: testSubscript
.test0() // expect: test0
other[0]. // expect: testSubscript
test0() // expect: test0

View File

@ -1,21 +0,0 @@
// Test the basic states. Keys without a group
// go into a group with null as the key
#!key
class Attr {}
System.print(Attr.attributes != null) // expect: true
System.print(Attr.attributes.self != null) // expect: true
System.print(Attr.attributes.methods) // expect: null
var attr = Attr.attributes.self
var nullGroup = attr[null]
System.print(nullGroup != null) // expect: true
System.print(nullGroup.count) // expect: 1
System.print(nullGroup.containsKey("key")) // expect: true
var keyItems = nullGroup["key"]
System.print(keyItems != null) // expect: true
System.print(keyItems is List) // expect: true
System.print(keyItems.count) // expect: 1
System.print(keyItems[0]) // expect: null

View File

@ -1,9 +0,0 @@
// Attributes without a ! shouldn't be
// passed to the runtime, they're compiled out
#compileonly
class WithNonRuntime {
#unused
method() {}
}
System.print(WithNonRuntime.attributes == null) // expect: true

View File

@ -1,11 +0,0 @@
// Duplicate keys add multiple values to
// the attribute's key, in parse order
#!key
#!key = value
#!key=other
class DuplicateKeys {}
var dupeGroup = DuplicateKeys.attributes.self[null]
System.print(dupeGroup.count) // expect: 1
System.print(dupeGroup["key"].count) // expect: 3
System.print(dupeGroup["key"]) // expect: [null, value, other]

View File

@ -1,17 +0,0 @@
// Groups store attributes by named group
#!key //not combined
#!group(key=combined)
#!group(key=value, key=2, key=false)
class GroupedKeys {}
var ungroupedKeys = GroupedKeys.attributes.self[null]
var groupedKeys = GroupedKeys.attributes.self["group"]
System.print(ungroupedKeys.count) // expect: 1
System.print(groupedKeys.count) // expect: 1
System.print(ungroupedKeys.containsKey("key")) // expect: true
var groupedKey = groupedKeys["key"]
System.print(groupedKey.count) // expect: 4
System.print(groupedKey) // expect: [combined, value, 2, false]

View File

@ -1,12 +0,0 @@
// When used in an expression location,
// the error remains Error at '#': Expected expression
#valid
class Example {
#valid
method() {
return #invalid 1 // expect error
}
}

View File

@ -1,11 +0,0 @@
#valid
class Example {
#valid
method() {
#invalid // expect error
var a = 3
}
}

View File

@ -1,4 +0,0 @@
#meta // expect error
var A = 3

View File

@ -1,20 +0,0 @@
// Keys must be a name, and values can be any literal value
#!name = name
#!string = "string"
#!integer = 32
#!number = 2.5
#!bool = true
class Literals {}
var literalGroup = Literals.attributes.self[null]
System.print(literalGroup.count) // expect: 5
System.print(literalGroup["string"][0] is String) // expect: true
System.print(literalGroup["string"][0]) // expect: string
System.print(literalGroup["integer"][0] is Num) // expect: true
System.print(literalGroup["integer"][0]) // expect: 32
System.print(literalGroup["number"][0] is Num) // expect: true
System.print(literalGroup["number"][0]) // expect: 2.5
System.print(literalGroup["bool"][0] is Bool) // expect: true
System.print(literalGroup["bool"][0]) // expect: true

View File

@ -1,32 +0,0 @@
class Methods {
#!getter
method {}
method() {}
#!regular = 2
#!group(key, other=value, string="hello")
method(arg0, arg1) {}
#!is_static = true
static method() {}
}
var methodAttr = Methods.attributes.methods
var getter = methodAttr["method"]
var none = methodAttr["method()"]
var regular = methodAttr["method(_,_)"]
var aStatic = methodAttr["static method()"]
// (Be wary of relying on map order)
System.print(getter) // expect: {null: {getter: [null]}}
System.print(none) // expect: null
System.print(regular[null]) // expect: {regular: [2]}
System.print(regular["group"]["key"]) // expect: [null]
System.print(regular["group"]["other"]) // expect: [value]
System.print(regular["group"]["string"]) // expect: [hello]
System.print(aStatic[null]) // expect: {is_static: [true]}

View File

@ -1,4 +0,0 @@
// With no attributes defined, no ClassAttributes should be allocated
class Without {}
System.print(Without.attributes == null) // expect: true

Some files were not shown because too many files have changed in this diff Show More