mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-12 06:38:45 +01:00
Compare commits
5 Commits
0.4.0
...
load-modul
| Author | SHA1 | Date | |
|---|---|---|---|
| 64c799e39a | |||
| 8d0074634f | |||
| 559ee1a4ca | |||
| 1a95253824 | |||
| 6d3739af65 |
6
AUTHORS
6
AUTHORS
@ -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>
|
||||
51
CHANGELOG.md
51
CHANGELOG.md
@ -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,
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -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
|
||||
|
||||
@ -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]!
|
||||
|
||||
[](https://travis-ci.org/wren-lang/wren)
|
||||
[](https://travis-ci.org/wren-lang/wren)
|
||||
|
||||
[syntax]: http://wren.io/syntax.html
|
||||
[src]: https://github.com/wren-lang/wren/tree/main/src
|
||||
|
||||
@ -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 →</a>
|
||||
<a href="functions.html">← Functions</a>
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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**
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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**
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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—they short-circuit.
|
||||
only conditionally evaluate right operand—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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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...
|
||||
|
||||
@ -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—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)—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—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 →</a>
|
||||
<a href="variables.html">← Variables</a>
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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 →</a>
|
||||
<a href="lists.html">← Lists</a>
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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**
|
||||
|
||||
@ -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>
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 ∞.
|
||||
|
||||
### Num.**nan**
|
||||
|
||||
One value representing a NaN.
|
||||
|
||||
Provides a default NaN number suitable for the vm internal values.
|
||||
|
||||
### Num.**pi**
|
||||
|
||||
The value of π.
|
||||
|
||||
### Num.**tau**
|
||||
|
||||
The value of τ. 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` (Euler’s 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.
|
||||
|
||||
### **<<**(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.
|
||||
|
||||
### **>>**(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>
|
||||
@ -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`.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
@ -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.
@ -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:
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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][].
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
@ -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);
|
||||
|
||||
@ -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)" }
|
||||
}
|
||||
@ -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";
|
||||
|
||||
|
||||
@ -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:
|
||||
{
|
||||
|
||||
@ -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
|
||||
@ -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)
|
||||
|
||||
@ -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.");
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
163
src/vm/wren_vm.c
163
src/vm/wren_vm.c
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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))
|
||||
@ -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__':
|
||||
|
||||
@ -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))
|
||||
@ -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))
|
||||
@ -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))
|
||||
@ -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))
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1,3 +0,0 @@
|
||||
var f1 = Fn.new {|a, b| a + b } // expect runtime error: Bool does not implement '+(_)'.
|
||||
f1.call(true, false)
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -1 +0,0 @@
|
||||
0.atan(false) // expect runtime error: x value must be a number.
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1 +0,0 @@
|
||||
1.clamp(0, false) // expect runtime error: Max value must be a number.
|
||||
@ -1 +0,0 @@
|
||||
1.clamp(false, 2) // expect runtime error: Min value must be a number.
|
||||
@ -1 +0,0 @@
|
||||
System.print(Num.maxSafeInteger) // expect: 9.007199254741e+15
|
||||
@ -1 +0,0 @@
|
||||
1.max(false) // expect runtime error: Other value must be a number.
|
||||
@ -1 +0,0 @@
|
||||
System.print(Num.minSafeInteger) // expect: -9.007199254741e+15
|
||||
@ -1,5 +0,0 @@
|
||||
var num = 4
|
||||
var num2 = 6
|
||||
|
||||
System.print(num.max(num2)) // expect: 6
|
||||
System.print(num.min(num2)) // expect: 4
|
||||
@ -1 +0,0 @@
|
||||
1.min(false) // expect runtime error: Other value must be a number.
|
||||
@ -1,2 +0,0 @@
|
||||
1.pow(false) // expect runtime error: Power value must be a number.
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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]
|
||||
@ -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]
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
|
||||
|
||||
#valid
|
||||
class Example {
|
||||
|
||||
#valid
|
||||
method() {
|
||||
#invalid // expect error
|
||||
var a = 3
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
|
||||
|
||||
#meta // expect error
|
||||
var A = 3
|
||||
@ -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
|
||||
@ -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]}
|
||||
@ -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
Reference in New Issue
Block a user