mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-12 22:58:40 +01:00
Compare commits
45 Commits
0.3.0
...
stack-corr
| Author | SHA1 | Date | |
|---|---|---|---|
| 48a6a5f67a | |||
| f769599bc6 | |||
| 039150efeb | |||
| 81bfbfce23 | |||
| dead8df82e | |||
| b279e51fd1 | |||
| 286162365a | |||
| 5b0f8740f2 | |||
| f81cb5d23c | |||
| 54b4c233b9 | |||
| 58611240e7 | |||
| a3f5b3d98f | |||
| da091e250c | |||
| 2ce421eac5 | |||
| d432b03d62 | |||
| 433fbc4019 | |||
| 0e8d56f874 | |||
| 28ad8aa9e0 | |||
| b3d496ea36 | |||
| 8be40ec14e | |||
| 1623654465 | |||
| e539279121 | |||
| 7651459dfb | |||
| d02903b7d0 | |||
| 88043a7cb9 | |||
| b59c060ccd | |||
| 30b2ebd3f7 | |||
| de6a312868 | |||
| 344d3432b3 | |||
| 7983082b71 | |||
| f3493d0499 | |||
| fea0dfafa0 | |||
| f894273f50 | |||
| 909d1c9471 | |||
| b5894c6ff5 | |||
| 7c357e1b02 | |||
| 9fb6d02b5c | |||
| e45a9d0382 | |||
| bef4099101 | |||
| 9f64c05fa8 | |||
| 26d0194117 | |||
| 2c2f5936eb | |||
| b694b2231c | |||
| 6cfe6dd6de | |||
| 8341f61cdb |
6
.gitignore
vendored
6
.gitignore
vendored
@ -16,6 +16,9 @@ Release/
|
||||
# The baseline file is machine-specific
|
||||
/test/benchmark/baseline.txt
|
||||
|
||||
# VSCode project files.
|
||||
.vscode
|
||||
|
||||
# XCode user-specific stuff
|
||||
xcuserdata/
|
||||
|
||||
@ -33,3 +36,6 @@ ipch/
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
25
.travis.sh
25
.travis.sh
@ -3,28 +3,23 @@ set -e
|
||||
|
||||
# This build script only builds mac or linux right now, for CI.
|
||||
WREN_WD="projects/make"
|
||||
if [ -n "$WREN_TARGET_MAC" ]
|
||||
then
|
||||
if [ -n "$WREN_TARGET_MAC" ]; then
|
||||
WREN_WD="projects/make.mac"
|
||||
fi
|
||||
|
||||
WREN_PY="python3"
|
||||
if [ -n "$WREN_PY_BINARY" ]
|
||||
then
|
||||
WREN_PY="$WREN_PY_BINARY"
|
||||
fi
|
||||
WREN_PY=${WREN_PY_BINARY:-python3}
|
||||
|
||||
echo "using working directory '$WREN_WD' ..."
|
||||
echo "using python binary '$WREN_PY' ..."
|
||||
|
||||
cd "$WREN_WD" && make config=debug_64bit-no-nan-tagging
|
||||
cd ../../ && $WREN_PY ./util/test.py --suffix=_d
|
||||
make -C $WREN_WD config=debug_64bit-no-nan-tagging
|
||||
$WREN_PY ./util/test.py --suffix=_d
|
||||
|
||||
cd "$WREN_WD" && make config=debug_64bit
|
||||
cd ../../ && $WREN_PY ./util/test.py --suffix=_d
|
||||
make -C $WREN_WD config=debug_64bit
|
||||
$WREN_PY ./util/test.py --suffix=_d
|
||||
|
||||
cd "$WREN_WD" && make config=release_64bit-no-nan-tagging
|
||||
cd ../../ && $WREN_PY ./util/test.py
|
||||
make -C $WREN_WD config=release_64bit-no-nan-tagging
|
||||
$WREN_PY ./util/test.py
|
||||
|
||||
cd "$WREN_WD" && make config=release_64bit
|
||||
cd ../../ && $WREN_PY ./util/test.py
|
||||
make -C $WREN_WD config=release_64bit
|
||||
$WREN_PY ./util/test.py
|
||||
|
||||
@ -16,8 +16,8 @@ jobs:
|
||||
|
||||
- stage: deploy
|
||||
script: ./util/deploy_docs_from_travis.sh
|
||||
# Only deploy commits that land on master.
|
||||
if: branch = master and type = push
|
||||
# Only deploy commits that land on main.
|
||||
if: branch = main and type = push
|
||||
|
||||
# Travis VMs are 64-bit but we compile both for 32 and 64 bit. To enable the
|
||||
# 32-bit builds to work, we need gcc-multilib.
|
||||
|
||||
22
CHANGELOG.md
22
CHANGELOG.md
@ -1,3 +1,21 @@
|
||||
## 0.4.0
|
||||
|
||||
- Fix some stack corruption with multiple wrenInterpret calls
|
||||
- WREN_MAX_TEMP_ROOTS default is 8 (instead of 5)
|
||||
- Add `Num.log2` and `Num.exp`
|
||||
- Fixed crash when GC collects module during import 2ce421ea
|
||||
- Fixed bug with `import` (28ad8aa9)
|
||||
- Map API support:
|
||||
- wrenSetSlotNewMap
|
||||
- wrenGetMapCount
|
||||
- wrenGetMapContainsKey
|
||||
- wrenGetMapValue
|
||||
- wrenSetMapValue
|
||||
- wrenRemoveMapValue
|
||||
- Add util/generate_projects.py (expects premake binary in projects/premake/)
|
||||
- Support positive sign (+) in scientific notation
|
||||
- Optional Random.Sample optimization (#716)
|
||||
|
||||
## 0.3.0
|
||||
|
||||
0.3.0 is a fairly specific release, aimed at fixing build issues across platforms,
|
||||
@ -13,7 +31,7 @@ across all platforms out of the box too (including the tests, benchmarks, metric
|
||||
Like before, there was some things that didn't hold up on Windows or Mac. Fixed!
|
||||
|
||||
A lot of work has been done to also clarify the distinction between the CLI project and the VM,
|
||||
as well as [move the CLI to it's own repo](https://github.com/wren-lang/wren-cli/)!
|
||||
as well as [move the CLI to its own repo](https://github.com/wren-lang/wren-cli/)!
|
||||
This removes a lot of code that wasn't being used, and also been clarified the project structure.
|
||||
|
||||
Docs have also had a clean up, and a new page to try Wren directly on the doc page was added.
|
||||
@ -33,7 +51,7 @@ Docs have also had a clean up, and a new page to try Wren directly on the doc pa
|
||||
|
||||
## 0.2.0
|
||||
|
||||
0.2.0 spans a pretty wide time period with [around 290 commits](https://github.com/wren-lang/wren/compare/0.1.0...master).
|
||||
0.2.0 spans a pretty wide time period with [around 290 commits](https://github.com/wren-lang/wren/compare/0.1.0...main).
|
||||
This includes many bug fixes, improvements, clarity in the
|
||||
code and documentation and so on. There's too many to explicitly list.
|
||||
Below is the obvious user facing stuff that was easy to spot in the history.
|
||||
|
||||
@ -47,7 +47,7 @@ involved][contribute]!
|
||||
[](https://travis-ci.org/wren-lang/wren)
|
||||
|
||||
[syntax]: http://wren.io/syntax.html
|
||||
[src]: https://github.com/wren-lang/wren/tree/master/src
|
||||
[src]: https://github.com/wren-lang/wren/tree/main/src
|
||||
[nan]: https://github.com/wren-lang/wren/blob/93dac9132773c5bc0bbe92df5ccbff14da9d25a6/src/vm/wren_value.h#L486-L541
|
||||
[perf]: http://wren.io/performance.html
|
||||
[classes]: http://wren.io/classes.html
|
||||
|
||||
@ -410,7 +410,7 @@ import strings will mean something different. We definitely *can* and will make
|
||||
breaking changes in Wren, so that's OK, but I'd like to minimize the pain. Right
|
||||
now, Wren is currently at version 0.1.0. I'll probably consider the commit right
|
||||
before I start landing this to be the "official" 0.1.0 release and then the
|
||||
import changes will land in "0.2.0". I'll work in a branch off master until
|
||||
import changes will land in "0.2.0". I'll work in a branch off main until
|
||||
everything looks solid and then merge it in.
|
||||
|
||||
If you have existing Wren code that you run on the CLI and that contains
|
||||
|
||||
@ -25,7 +25,7 @@ Wren attracted me as a language because of what it _is_, not because it isn't {_
|
||||
|
||||
So, Wren is going to be changing in ways that align with it's existing design intentions. Staying small, simple, learnable and hackable is all vital to what makes Wren valuable, and will remain.
|
||||
|
||||
We're just as excited as you are to get to longer term changes and fun tweaks (we have lots of work done already in local projects like [the debugger](https://i.imgur.com/dazexnY.gifv)). There's plenty of ideas we've tried since we've been [using Wren full time](https://luxeengine.com) the last 2.5+ years, and can't wait to get started with bring those into master (and optional modules). There's a lot to do!
|
||||
We're just as excited as you are to get to longer term changes and fun tweaks (we have lots of work done already in local projects like [the debugger](https://i.imgur.com/dazexnY.gifv)). There's plenty of ideas we've tried since we've been [using Wren full time](https://luxeengine.com) the last 2.5+ years, and can't wait to get started with bring those into the main branch (and optional modules). There's a lot to do!
|
||||
|
||||
In the next blog I want to try talk a bit more about the short to medium term goals and roadmap (rather than mixing it here with the meta/hello world post). Be sure to keep an eye out for that one, as it has more juicy details on what we're gonna get up to.
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ and mark what we managed to get done:
|
||||
With 0.3.0 we've separated the CLI from the Wren repo,
|
||||
and updated the docs to make the distinction clearer.
|
||||
|
||||
The [CLI now has it's own corner of the docs](../cli), so that the modules
|
||||
The [CLI now has its own corner of the docs](../cli), so that the modules
|
||||
and API docs aren't overlapped like before. This opens up space for the
|
||||
CLI to get better, fuller documentation, and removes confusion about
|
||||
built in modules vs ones that are in the CLI only.
|
||||
|
||||
@ -34,8 +34,14 @@
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="getting-started.html">Getting Started</a></td>
|
||||
<td><a href="contributing.html">Contributing</a></td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
@ -51,7 +57,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -14,8 +14,8 @@ That includes the built in language features, the standard library and the VM it
|
||||
|
||||
In order to access files, networks and other IO, you'd need to make a tool _using_ the language VM.
|
||||
That's what the CLI project is! It is not bundled as part of the wren project,
|
||||
instead it is it's own project as a standalone tool you can run.
|
||||
It exposes it's own standard library and modules that may be of interest
|
||||
instead it is its own project as a standalone tool you can run.
|
||||
It exposes its own standard library and modules that may be of interest
|
||||
if looking for a general purpose single binary scriptable tool.
|
||||
|
||||
Wren CLI is a work in progress, and contributions are welcome to make it more useful over time.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
^title CLI Modules
|
||||
|
||||
The Wren CLI executable extends the built in language modules with it's own,
|
||||
The Wren CLI executable extends the built in language modules with its own,
|
||||
which offer access to IO and other facilities for scripting.
|
||||
|
||||
The CLI modules are deeply tied to [libuv][], each other, and other internals
|
||||
|
||||
@ -80,7 +80,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -72,7 +72,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -70,7 +70,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -85,7 +85,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -70,7 +70,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -7,7 +7,7 @@ You can [download a build for your OS from the releases page](https://github.com
|
||||
|
||||
### Interactive mode
|
||||
|
||||
If you just run `wren` without any arguments, it starts the interpreter in
|
||||
If you just run `wren_cli` without any arguments, it starts the interpreter in
|
||||
interactive mode, where you can type in a line of code, and it immediately executes
|
||||
it. You can exit the interpreter using good old Ctrl-C or Ctrl-D.
|
||||
|
||||
@ -26,7 +26,7 @@ for (i in 1..10) System.print("Counting up %(i)")
|
||||
### Running scripts
|
||||
|
||||
The standalone interpreter can also load scripts from files and run them. Just
|
||||
pass the name of the script to `wren`. Create a file named "my_script.wren" in
|
||||
pass the name of the script to `wren_cli`. Create a file named "my_script.wren" in
|
||||
your favorite text editor and paste this into it:
|
||||
|
||||
<pre class="snippet">
|
||||
@ -52,4 +52,4 @@ for (yPixel in 0...24) {
|
||||
|
||||
Now run:
|
||||
|
||||
$ ./wren my_script.wren
|
||||
$ ./wren_cli my_script.wren
|
||||
|
||||
@ -118,11 +118,11 @@ on the [discord community][discord] (or the [mailing list][list]). If you're a R
|
||||
[mit]: http://opensource.org/licenses/MIT
|
||||
[github]: https://github.com/wren-lang/
|
||||
[fork]: https://help.github.com/articles/fork-a-repo/
|
||||
[docs]: https://github.com/wren-lang/wren/tree/master/doc/site
|
||||
[docs]: https://github.com/wren-lang/wren/tree/main/doc/site
|
||||
[issue]: https://github.com/wren-lang/wren/issues
|
||||
[proposal]: https://github.com/wren-lang/wren/labels/proposal
|
||||
[feature branches]: https://www.atlassian.com/git/tutorials/comparing-workflows/centralized-workflow
|
||||
[authors]: https://github.com/wren-lang/wren/tree/master/AUTHORS
|
||||
[authors]: https://github.com/wren-lang/wren/tree/main/AUTHORS
|
||||
[pull request]: https://github.com/wren-lang/wren/pulls
|
||||
[list]: https://groups.google.com/forum/#!forum/wren-lang
|
||||
[subreddit]: https://www.reddit.com/r/wren_lang/
|
||||
|
||||
@ -79,7 +79,7 @@ There are two (well, three) ways to get the Wren VM into your program:
|
||||
In either case, you also want to add `src/include` to your include path so you
|
||||
can find the [public header for Wren][wren.h]:
|
||||
|
||||
[wren.h]: https://github.com/wren-lang/wren/blob/master/src/include/wren.h
|
||||
[wren.h]: https://github.com/wren-lang/wren/blob/main/src/include/wren.h
|
||||
|
||||
<pre class="snippet" data-lang="c">
|
||||
#include "wren.h"
|
||||
@ -102,13 +102,35 @@ this header handles the differences in calling conventions between C and C++:
|
||||
## Creating a Wren VM
|
||||
|
||||
Once you've integrated the code into your executable, you need to create a
|
||||
virtual machine. To do that, you create a
|
||||
virtual machine. To do that, you create a `WrenConfiguration` object and
|
||||
initialize it.
|
||||
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
</pre>
|
||||
|
||||
This gives you a basic configuration that has reasonable defaults for
|
||||
everything. If you don't need to tweak stuff, you can leave it at that. We'll
|
||||
[learn more][configuration] about what you can configure later.
|
||||
everything. We'll [learn more][configuration] about what you can configure later,
|
||||
but for now we'll just add the `writeFn`, so that we can print text.
|
||||
|
||||
First we need a function that will do something with the output
|
||||
that Wren sends us from `System.print` (or `System.write`). *Note that it doesn't
|
||||
include a newline in the output.*
|
||||
|
||||
<pre class="snippet" data-lang="c">
|
||||
void writeFn(WrenVM* vm, const char* text) {
|
||||
printf("%s", text);
|
||||
}
|
||||
</pre>
|
||||
|
||||
And then, we update the configuration to point to it.
|
||||
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
config.writeFn = &writeFn;
|
||||
</pre>
|
||||
|
||||
[configuration]: configuring-the-vm.html
|
||||
|
||||
@ -178,6 +200,71 @@ 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...
|
||||
|
||||
@ -12,7 +12,7 @@ written in C. It produces real Wren objects that you can pass around, do `is`
|
||||
checks on, etc. But it also wraps a blob of raw memory that is opaque to Wren
|
||||
but accessible from C.
|
||||
|
||||
[foreign methods]: http://localhost:8000/embedding/calling-c-from-wren.html
|
||||
[foreign methods]: calling-c-from-wren.html
|
||||
|
||||
## Defining a Foreign Class
|
||||
|
||||
|
||||
@ -80,7 +80,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -51,7 +51,7 @@ If you like the sound of this, [let's get started][started].
|
||||
Excited? You're also welcome to [get involved][contribute]!
|
||||
|
||||
[syntax]: syntax.html
|
||||
[src]: https://github.com/wren-lang/wren/tree/master/src
|
||||
[src]: https://github.com/wren-lang/wren/tree/main/src
|
||||
[nan]: https://github.com/wren-lang/wren/blob/46c1ba92492e9257aba6418403161072d640cb29/src/wren_value.h#L378-L433
|
||||
[perf]: performance.html
|
||||
[classes]: classes.html
|
||||
|
||||
@ -96,7 +96,15 @@ Whether the number is [not a number](http://en.wikipedia.org/wiki/NaN). This is
|
||||
|
||||
### **log**
|
||||
|
||||
The natural logarithm of the number.
|
||||
The natural logarithm of the number. Returns `nan` if the base is negative.
|
||||
|
||||
### **log2**
|
||||
|
||||
The binary (base-2) logarithm of the number. Returns `nan` if the base is negative.
|
||||
|
||||
### **exp**
|
||||
|
||||
The exponential `e` (Euler’s number) raised to the number. This: `eⁿ`.
|
||||
|
||||
### **pow**(power)
|
||||
|
||||
|
||||
@ -92,7 +92,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -15,6 +15,7 @@ It must be imported from the [random][] module:
|
||||
|
||||
<pre class="snippet">
|
||||
import "random" for Random
|
||||
</pre>
|
||||
|
||||
[random]: ../
|
||||
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -98,7 +98,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -92,7 +92,7 @@ languages benchmarked, including Wren, because Mike Pall is a robot from the
|
||||
future.
|
||||
|
||||
The benchmark harness and programs are
|
||||
[here](https://github.com/wren-lang/wren/tree/master/test/benchmark).
|
||||
[here](https://github.com/wren-lang/wren/tree/main/test/benchmark).
|
||||
|
||||
## Why is Wren fast?
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -120,7 +120,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -121,7 +121,7 @@
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
|
||||
@ -19,6 +19,7 @@ Like other scripting languages, Wren has a single numeric type:
|
||||
double-precision floating point. Number literals look like you expect coming
|
||||
from other languages:
|
||||
|
||||
|
||||
<pre class="snippet">
|
||||
0
|
||||
1234
|
||||
@ -26,6 +27,9 @@ from other languages:
|
||||
3.14159
|
||||
1.0
|
||||
-12.34
|
||||
0.0314159e02
|
||||
0.0314159e+02
|
||||
314.159e-02
|
||||
</pre>
|
||||
|
||||
Numbers are instances of the [Num][] class.
|
||||
|
||||
58
example/embedding/main.c
Normal file
58
example/embedding/main.c
Normal file
@ -0,0 +1,58 @@
|
||||
//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);
|
||||
|
||||
}
|
||||
@ -121,6 +121,7 @@ OBJECTS += $(OBJDIR)/get_variable.o
|
||||
OBJECTS += $(OBJDIR)/handle.o
|
||||
OBJECTS += $(OBJDIR)/lists.o
|
||||
OBJECTS += $(OBJDIR)/main.o
|
||||
OBJECTS += $(OBJDIR)/maps.o
|
||||
OBJECTS += $(OBJDIR)/new_vm.o
|
||||
OBJECTS += $(OBJDIR)/reset_stack_after_call_abort.o
|
||||
OBJECTS += $(OBJDIR)/reset_stack_after_foreign_construct.o
|
||||
@ -219,6 +220,9 @@ $(OBJDIR)/handle.o: ../../test/api/handle.c
|
||||
$(OBJDIR)/lists.o: ../../test/api/lists.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/maps.o: ../../test/api/maps.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/new_vm.o: ../../test/api/new_vm.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
||||
@ -129,6 +129,7 @@ OBJECTS += $(OBJDIR)/get_variable.o
|
||||
OBJECTS += $(OBJDIR)/handle.o
|
||||
OBJECTS += $(OBJDIR)/lists.o
|
||||
OBJECTS += $(OBJDIR)/main.o
|
||||
OBJECTS += $(OBJDIR)/maps.o
|
||||
OBJECTS += $(OBJDIR)/new_vm.o
|
||||
OBJECTS += $(OBJDIR)/reset_stack_after_call_abort.o
|
||||
OBJECTS += $(OBJDIR)/reset_stack_after_foreign_construct.o
|
||||
@ -227,6 +228,9 @@ $(OBJDIR)/handle.o: ../../test/api/handle.c
|
||||
$(OBJDIR)/lists.o: ../../test/api/lists.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/maps.o: ../../test/api/maps.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/new_vm.o: ../../test/api/new_vm.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
||||
@ -121,6 +121,7 @@ OBJECTS += $(OBJDIR)/get_variable.o
|
||||
OBJECTS += $(OBJDIR)/handle.o
|
||||
OBJECTS += $(OBJDIR)/lists.o
|
||||
OBJECTS += $(OBJDIR)/main.o
|
||||
OBJECTS += $(OBJDIR)/maps.o
|
||||
OBJECTS += $(OBJDIR)/new_vm.o
|
||||
OBJECTS += $(OBJDIR)/reset_stack_after_call_abort.o
|
||||
OBJECTS += $(OBJDIR)/reset_stack_after_foreign_construct.o
|
||||
@ -219,6 +220,9 @@ $(OBJDIR)/handle.o: ../../test/api/handle.c
|
||||
$(OBJDIR)/lists.o: ../../test/api/lists.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/maps.o: ../../test/api/maps.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/new_vm.o: ../../test/api/new_vm.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
||||
BIN
projects/premake/premake5.exe
Normal file
BIN
projects/premake/premake5.exe
Normal file
Binary file not shown.
@ -265,6 +265,7 @@
|
||||
<ClInclude Include="..\..\test\api\get_variable.h" />
|
||||
<ClInclude Include="..\..\test\api\handle.h" />
|
||||
<ClInclude Include="..\..\test\api\lists.h" />
|
||||
<ClInclude Include="..\..\test\api\maps.h" />
|
||||
<ClInclude Include="..\..\test\api\new_vm.h" />
|
||||
<ClInclude Include="..\..\test\api\reset_stack_after_call_abort.h" />
|
||||
<ClInclude Include="..\..\test\api\reset_stack_after_foreign_construct.h" />
|
||||
@ -284,6 +285,7 @@
|
||||
<ClCompile Include="..\..\test\api\get_variable.c" />
|
||||
<ClCompile Include="..\..\test\api\handle.c" />
|
||||
<ClCompile Include="..\..\test\api\lists.c" />
|
||||
<ClCompile Include="..\..\test\api\maps.c" />
|
||||
<ClCompile Include="..\..\test\api\new_vm.c" />
|
||||
<ClCompile Include="..\..\test\api\reset_stack_after_call_abort.c" />
|
||||
<ClCompile Include="..\..\test\api\reset_stack_after_foreign_construct.c" />
|
||||
|
||||
@ -36,6 +36,9 @@
|
||||
<ClInclude Include="..\..\test\api\lists.h">
|
||||
<Filter>api</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test\api\maps.h">
|
||||
<Filter>api</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test\api\new_vm.h">
|
||||
<Filter>api</Filter>
|
||||
</ClInclude>
|
||||
@ -87,6 +90,9 @@
|
||||
<ClCompile Include="..\..\test\api\lists.c">
|
||||
<Filter>api</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test\api\maps.c">
|
||||
<Filter>api</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test\api\new_vm.c">
|
||||
<Filter>api</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@ -265,6 +265,7 @@
|
||||
<ClInclude Include="..\..\test\api\get_variable.h" />
|
||||
<ClInclude Include="..\..\test\api\handle.h" />
|
||||
<ClInclude Include="..\..\test\api\lists.h" />
|
||||
<ClInclude Include="..\..\test\api\maps.h" />
|
||||
<ClInclude Include="..\..\test\api\new_vm.h" />
|
||||
<ClInclude Include="..\..\test\api\reset_stack_after_call_abort.h" />
|
||||
<ClInclude Include="..\..\test\api\reset_stack_after_foreign_construct.h" />
|
||||
@ -284,6 +285,7 @@
|
||||
<ClCompile Include="..\..\test\api\get_variable.c" />
|
||||
<ClCompile Include="..\..\test\api\handle.c" />
|
||||
<ClCompile Include="..\..\test\api\lists.c" />
|
||||
<ClCompile Include="..\..\test\api\maps.c" />
|
||||
<ClCompile Include="..\..\test\api\new_vm.c" />
|
||||
<ClCompile Include="..\..\test\api\reset_stack_after_call_abort.c" />
|
||||
<ClCompile Include="..\..\test\api\reset_stack_after_foreign_construct.c" />
|
||||
|
||||
@ -36,6 +36,9 @@
|
||||
<ClInclude Include="..\..\test\api\lists.h">
|
||||
<Filter>api</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test\api\maps.h">
|
||||
<Filter>api</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test\api\new_vm.h">
|
||||
<Filter>api</Filter>
|
||||
</ClInclude>
|
||||
@ -87,6 +90,9 @@
|
||||
<ClCompile Include="..\..\test\api\lists.c">
|
||||
<Filter>api</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test\api\maps.c">
|
||||
<Filter>api</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test\api\new_vm.c">
|
||||
<Filter>api</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
31D07E222B367F941ED1DC62 /* test.c in Sources */ = {isa = PBXBuildFile; fileRef = 7B2F762A1E7AFF5C394BCC6A /* test.c */; };
|
||||
47E53E839E72A8F576EBBCC3 /* call.c in Sources */ = {isa = PBXBuildFile; fileRef = 2F776B0B32E83D3DB454E14B /* call.c */; };
|
||||
4D8AE463A8CC37D57C2682A3 /* handle.c in Sources */ = {isa = PBXBuildFile; fileRef = 069BFCEB1DAE981D0DCA932B /* handle.c */; };
|
||||
4EA04F0DA52DB97F7DA6CD4D /* maps.c in Sources */ = {isa = PBXBuildFile; fileRef = 3AEABBF53E5B8E27BFC83235 /* maps.c */; };
|
||||
59615B339DB000A5DFD73973 /* resolution.c in Sources */ = {isa = PBXBuildFile; fileRef = 535262BB751E0FEDB1D338FB /* resolution.c */; };
|
||||
78667B8C71CC7CFE6567D9CC /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 6E0F7DF4115B07262C2BD434 /* main.c */; };
|
||||
7CCD7163EDF01255A3B6BFA3 /* api_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 21B810EB49F2C99D318E572B /* api_tests.c */; };
|
||||
@ -58,6 +59,7 @@
|
||||
30E3DEE9D44B0C9BEB80C529 /* reset_stack_after_foreign_construct.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = reset_stack_after_foreign_construct.h; path = ../../test/api/reset_stack_after_foreign_construct.h; sourceTree = "<group>"; };
|
||||
310165BFD4689371EB9E4BFF /* reset_stack_after_foreign_construct.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = reset_stack_after_foreign_construct.c; path = ../../test/api/reset_stack_after_foreign_construct.c; sourceTree = "<group>"; };
|
||||
33526D1DD64093CF6566735D /* slots.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = slots.c; path = ../../test/api/slots.c; sourceTree = "<group>"; };
|
||||
3AEABBF53E5B8E27BFC83235 /* maps.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = maps.c; path = ../../test/api/maps.c; sourceTree = "<group>"; };
|
||||
3E23E7FBE1120EAD7037EE3B /* lists.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = lists.h; path = ../../test/api/lists.h; sourceTree = "<group>"; };
|
||||
41058591E3F3AC4373198BD1 /* lists.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = lists.c; path = ../../test/api/lists.c; sourceTree = "<group>"; };
|
||||
50338489786E3D3B6009CAC9 /* benchmark.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = benchmark.c; path = ../../test/api/benchmark.c; sourceTree = "<group>"; };
|
||||
@ -73,6 +75,7 @@
|
||||
9C375BF5B349F727A365F235 /* handle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = handle.h; path = ../../test/api/handle.h; sourceTree = "<group>"; };
|
||||
A415E4D5A786B70728F35B15 /* call.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = call.h; path = ../../test/api/call.h; sourceTree = "<group>"; };
|
||||
ABEF15744F3A9EA66A0B6BB4 /* test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = test.h; path = ../../test/test.h; sourceTree = "<group>"; };
|
||||
AF8935BFB2FA07F13466ABFF /* maps.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = maps.h; path = ../../test/api/maps.h; sourceTree = "<group>"; };
|
||||
B0175F83D8521835BFEDA5C3 /* user_data.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = user_data.c; path = ../../test/api/user_data.c; sourceTree = "<group>"; };
|
||||
B0267C45D1F229770EA75285 /* resolution.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = resolution.h; path = ../../test/api/resolution.h; sourceTree = "<group>"; };
|
||||
B1B12E89FA506CBB1D7C24C9 /* reset_stack_after_call_abort.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = reset_stack_after_call_abort.c; path = ../../test/api/reset_stack_after_call_abort.c; sourceTree = "<group>"; };
|
||||
@ -128,6 +131,8 @@
|
||||
9C375BF5B349F727A365F235 /* handle.h */,
|
||||
41058591E3F3AC4373198BD1 /* lists.c */,
|
||||
3E23E7FBE1120EAD7037EE3B /* lists.h */,
|
||||
3AEABBF53E5B8E27BFC83235 /* maps.c */,
|
||||
AF8935BFB2FA07F13466ABFF /* maps.h */,
|
||||
C22EBF6BD9415A9DC95D55AB /* new_vm.c */,
|
||||
57CA1E756EDCB9A75EF8B4B5 /* new_vm.h */,
|
||||
B1B12E89FA506CBB1D7C24C9 /* reset_stack_after_call_abort.c */,
|
||||
@ -259,6 +264,7 @@
|
||||
E21864B54F40732750D362F5 /* get_variable.c in Sources */,
|
||||
4D8AE463A8CC37D57C2682A3 /* handle.c in Sources */,
|
||||
1C5491694BE6605B26F39FA9 /* lists.c in Sources */,
|
||||
4EA04F0DA52DB97F7DA6CD4D /* maps.c in Sources */,
|
||||
29C70EE3850862555862AD23 /* new_vm.c in Sources */,
|
||||
BD0CC4E181C21B533630C321 /* reset_stack_after_call_abort.c in Sources */,
|
||||
8F510F77A173C5697A74FDB7 /* reset_stack_after_foreign_construct.c in Sources */,
|
||||
|
||||
@ -15,8 +15,8 @@
|
||||
|
||||
// A monotonically increasing numeric representation of the version number. Use
|
||||
// this if you want to do range checks over versions.
|
||||
#define WREN_VERSION_NUMBER (WREN_VERSION_MAJOR * 1000000 + \
|
||||
WREN_VERSION_MINOR * 1000 + \
|
||||
#define WREN_VERSION_NUMBER (WREN_VERSION_MAJOR * 1000000 + \
|
||||
WREN_VERSION_MINOR * 1000 + \
|
||||
WREN_VERSION_PATCH)
|
||||
|
||||
// A single virtual machine for executing Wren code.
|
||||
@ -263,6 +263,7 @@ typedef enum
|
||||
WREN_TYPE_NUM,
|
||||
WREN_TYPE_FOREIGN,
|
||||
WREN_TYPE_LIST,
|
||||
WREN_TYPE_MAP,
|
||||
WREN_TYPE_NULL,
|
||||
WREN_TYPE_STRING,
|
||||
|
||||
@ -440,6 +441,9 @@ void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size);
|
||||
// Stores a new empty list in [slot].
|
||||
void wrenSetSlotNewList(WrenVM* vm, int slot);
|
||||
|
||||
// Stores a new empty map in [slot].
|
||||
void wrenSetSlotNewMap(WrenVM* vm, int slot);
|
||||
|
||||
// Stores null in [slot].
|
||||
void wrenSetSlotNull(WrenVM* vm, int slot);
|
||||
|
||||
@ -470,6 +474,26 @@ void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);
|
||||
// an element, use `-1` for the index.
|
||||
void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot);
|
||||
|
||||
// Returns the number of entries in the map stored in [slot].
|
||||
int wrenGetMapCount(WrenVM* vm, int slot);
|
||||
|
||||
// Returns true if the key in [keySlot] is found in the map placed in [mapSlot].
|
||||
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].
|
||||
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].
|
||||
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.
|
||||
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].
|
||||
void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
|
||||
|
||||
@ -47,65 +47,38 @@ foreign class Random {
|
||||
int(end) { (float() * end).floor }
|
||||
int(start, end) { (float() * (end - start)).floor + start }
|
||||
|
||||
sample(list) { sample(list, 1)[0] }
|
||||
sample(list) {
|
||||
if (list.count == 0) Fiber.abort("Not enough elements to sample.")
|
||||
return list[int(list.count)]
|
||||
}
|
||||
sample(list, count) {
|
||||
if (count > list.count) Fiber.abort("Not enough elements to sample.")
|
||||
|
||||
// There are (at least) two simple algorithms for choosing a number of
|
||||
// samples from a list without replacement -- where we don't pick the same
|
||||
// element more than once.
|
||||
//
|
||||
// The first is faster when the number of samples is small relative to the
|
||||
// size of the collection. In many cases, it avoids scanning the entire
|
||||
// list. In the common case of just wanting one sample, it's a single
|
||||
// random index lookup.
|
||||
//
|
||||
// However, its performance degrades badly as the sample size increases.
|
||||
// Vitter's algorithm always scans the entire list, but it's also always
|
||||
// O(n).
|
||||
//
|
||||
// The cutoff point between the two follows a quadratic curve on the same
|
||||
// size. Based on some empirical testing, scaling that by 5 seems to fit
|
||||
// pretty closely and chooses the fastest one for the given sample and
|
||||
// collection size.
|
||||
if (count * count * 5 < list.count) {
|
||||
// Pick random elements and retry if you hit a previously chosen one.
|
||||
var picked = {}
|
||||
var result = []
|
||||
for (i in 0...count) {
|
||||
// Find an index that we haven't already selected.
|
||||
var index
|
||||
while (true) {
|
||||
index = int(list.count)
|
||||
if (!picked.containsKey(index)) break
|
||||
}
|
||||
var result = []
|
||||
|
||||
// The algorithm described in "Programming pearls: a sample of brilliance".
|
||||
// Use a hash map for sample sizes less than 1/4 of the population size and
|
||||
// an array of booleans for larger samples. This simple heuristic improves
|
||||
// performance for large sample sizes as well as reduces memory usage.
|
||||
if (count * 4 < list.count) {
|
||||
var picked = {}
|
||||
for (i in list.count - count...list.count) {
|
||||
var index = int(i + 1)
|
||||
if (picked.containsKey(index)) index = i
|
||||
picked[index] = true
|
||||
result.add(list[index])
|
||||
}
|
||||
|
||||
return result
|
||||
} else {
|
||||
// Jeffrey Vitter's Algorithm R.
|
||||
|
||||
// Fill the reservoir with the first elements in the list.
|
||||
var result = list[0...count]
|
||||
|
||||
// We want to ensure the results are always in random order, so shuffle
|
||||
// them. In cases where the sample size is the entire collection, this
|
||||
// devolves to running Fisher-Yates on a copy of the list.
|
||||
shuffle(result)
|
||||
|
||||
// Now walk the rest of the list. For each element, randomly consider
|
||||
// replacing one of the reservoir elements with it. The probability here
|
||||
// works out such that it does this uniformly.
|
||||
for (i in count...list.count) {
|
||||
var slot = int(0, i + 1)
|
||||
if (slot < count) result[slot] = list[i]
|
||||
var picked = List.filled(list.count, false)
|
||||
for (i in list.count - count...list.count) {
|
||||
var index = int(i + 1)
|
||||
if (picked[index]) index = i
|
||||
picked[index] = true
|
||||
result.add(list[index])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
shuffle(list) {
|
||||
|
||||
@ -49,65 +49,38 @@ static const char* randomModuleSource =
|
||||
" int(end) { (float() * end).floor }\n"
|
||||
" int(start, end) { (float() * (end - start)).floor + start }\n"
|
||||
"\n"
|
||||
" sample(list) { sample(list, 1)[0] }\n"
|
||||
" sample(list) {\n"
|
||||
" if (list.count == 0) Fiber.abort(\"Not enough elements to sample.\")\n"
|
||||
" return list[int(list.count)]\n"
|
||||
" }\n"
|
||||
" sample(list, count) {\n"
|
||||
" if (count > list.count) Fiber.abort(\"Not enough elements to sample.\")\n"
|
||||
"\n"
|
||||
" // There are (at least) two simple algorithms for choosing a number of\n"
|
||||
" // samples from a list without replacement -- where we don't pick the same\n"
|
||||
" // element more than once.\n"
|
||||
" //\n"
|
||||
" // The first is faster when the number of samples is small relative to the\n"
|
||||
" // size of the collection. In many cases, it avoids scanning the entire\n"
|
||||
" // list. In the common case of just wanting one sample, it's a single\n"
|
||||
" // random index lookup.\n"
|
||||
" //\n"
|
||||
" // However, its performance degrades badly as the sample size increases.\n"
|
||||
" // Vitter's algorithm always scans the entire list, but it's also always\n"
|
||||
" // O(n).\n"
|
||||
" //\n"
|
||||
" // The cutoff point between the two follows a quadratic curve on the same\n"
|
||||
" // size. Based on some empirical testing, scaling that by 5 seems to fit\n"
|
||||
" // pretty closely and chooses the fastest one for the given sample and\n"
|
||||
" // collection size.\n"
|
||||
" if (count * count * 5 < list.count) {\n"
|
||||
" // Pick random elements and retry if you hit a previously chosen one.\n"
|
||||
" var picked = {}\n"
|
||||
" var result = []\n"
|
||||
" for (i in 0...count) {\n"
|
||||
" // Find an index that we haven't already selected.\n"
|
||||
" var index\n"
|
||||
" while (true) {\n"
|
||||
" index = int(list.count)\n"
|
||||
" if (!picked.containsKey(index)) break\n"
|
||||
" }\n"
|
||||
" var result = []\n"
|
||||
"\n"
|
||||
" // The algorithm described in \"Programming pearls: a sample of brilliance\".\n"
|
||||
" // Use a hash map for sample sizes less than 1/4 of the population size and\n"
|
||||
" // an array of booleans for larger samples. This simple heuristic improves\n"
|
||||
" // performance for large sample sizes as well as reduces memory usage.\n"
|
||||
" if (count * 4 < list.count) {\n"
|
||||
" var picked = {}\n"
|
||||
" for (i in list.count - count...list.count) {\n"
|
||||
" var index = int(i + 1)\n"
|
||||
" if (picked.containsKey(index)) index = i\n"
|
||||
" picked[index] = true\n"
|
||||
" result.add(list[index])\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return result\n"
|
||||
" } else {\n"
|
||||
" // Jeffrey Vitter's Algorithm R.\n"
|
||||
"\n"
|
||||
" // Fill the reservoir with the first elements in the list.\n"
|
||||
" var result = list[0...count]\n"
|
||||
"\n"
|
||||
" // We want to ensure the results are always in random order, so shuffle\n"
|
||||
" // them. In cases where the sample size is the entire collection, this\n"
|
||||
" // devolves to running Fisher-Yates on a copy of the list.\n"
|
||||
" shuffle(result)\n"
|
||||
"\n"
|
||||
" // Now walk the rest of the list. For each element, randomly consider\n"
|
||||
" // replacing one of the reservoir elements with it. The probability here\n"
|
||||
" // works out such that it does this uniformly.\n"
|
||||
" for (i in count...list.count) {\n"
|
||||
" var slot = int(0, i + 1)\n"
|
||||
" if (slot < count) result[slot] = list[i]\n"
|
||||
" var picked = List.filled(list.count, false)\n"
|
||||
" for (i in list.count - count...list.count) {\n"
|
||||
" var index = int(i + 1)\n"
|
||||
" if (picked[index]) index = i\n"
|
||||
" picked[index] = true\n"
|
||||
" result.add(list[index])\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" shuffle(list) {\n"
|
||||
|
||||
@ -33,10 +33,10 @@
|
||||
// http://gcc.gnu.org/onlinedocs/gcc-3.1.1/gcc/Labels-as-Values.html
|
||||
// Enabling this speeds up the main dispatch loop a bit, but requires compiler
|
||||
// support.
|
||||
//
|
||||
// see https://bullno1.com/blog/switched-goto for alternative
|
||||
// Defaults to true on supported compilers.
|
||||
#ifndef WREN_COMPUTED_GOTO
|
||||
#ifdef _MSC_VER
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
// No computed gotos in Visual Studio.
|
||||
#define WREN_COMPUTED_GOTO 0
|
||||
#else
|
||||
@ -114,17 +114,17 @@
|
||||
#define MAX_FIELDS 255
|
||||
|
||||
// Use the VM's allocator to allocate an object of [type].
|
||||
#define ALLOCATE(vm, type) \
|
||||
#define ALLOCATE(vm, type) \
|
||||
((type*)wrenReallocate(vm, NULL, 0, sizeof(type)))
|
||||
|
||||
// Use the VM's allocator to allocate an object of [mainType] containing a
|
||||
// flexible array of [count] objects of [arrayType].
|
||||
#define ALLOCATE_FLEX(vm, mainType, arrayType, count) \
|
||||
((mainType*)wrenReallocate(vm, NULL, 0, \
|
||||
sizeof(mainType) + sizeof(arrayType) * (count)))
|
||||
#define ALLOCATE_FLEX(vm, mainType, arrayType, count) \
|
||||
((mainType*)wrenReallocate(vm, NULL, 0, \
|
||||
sizeof(mainType) + sizeof(arrayType) * (count)))
|
||||
|
||||
// Use the VM's allocator to allocate an array of [count] elements of [type].
|
||||
#define ALLOCATE_ARRAY(vm, type, count) \
|
||||
#define ALLOCATE_ARRAY(vm, type, count) \
|
||||
((type*)wrenReallocate(vm, NULL, 0, sizeof(type) * (count)))
|
||||
|
||||
// Use the VM's allocator to free the previously allocated memory at [pointer].
|
||||
@ -156,17 +156,16 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define ASSERT(condition, message) \
|
||||
do \
|
||||
{ \
|
||||
if (!(condition)) \
|
||||
{ \
|
||||
fprintf(stderr, "[%s:%d] Assert failed in %s(): %s\n", \
|
||||
__FILE__, __LINE__, __func__, message); \
|
||||
abort(); \
|
||||
} \
|
||||
} \
|
||||
while(0)
|
||||
#define ASSERT(condition, message) \
|
||||
do \
|
||||
{ \
|
||||
if (!(condition)) \
|
||||
{ \
|
||||
fprintf(stderr, "[%s:%d] Assert failed in %s(): %s\n", \
|
||||
__FILE__, __LINE__, __func__, message); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
// Indicates that we know execution should never reach this point in the
|
||||
// program. In debug mode, we assert this fact because it's a bug to get here.
|
||||
@ -175,18 +174,17 @@
|
||||
// compiler the code can't be reached. This avoids "missing return" warnings
|
||||
// in some cases and also lets it perform some optimizations by assuming the
|
||||
// code is never reached.
|
||||
#define UNREACHABLE() \
|
||||
do \
|
||||
{ \
|
||||
fprintf(stderr, "[%s:%d] This code should not be reached in %s()\n", \
|
||||
__FILE__, __LINE__, __func__); \
|
||||
abort(); \
|
||||
} \
|
||||
while (0)
|
||||
#define UNREACHABLE() \
|
||||
do \
|
||||
{ \
|
||||
fprintf(stderr, "[%s:%d] This code should not be reached in %s()\n", \
|
||||
__FILE__, __LINE__, __func__); \
|
||||
abort(); \
|
||||
} while (false)
|
||||
|
||||
#else
|
||||
|
||||
#define ASSERT(condition, message) do {} while (0)
|
||||
#define ASSERT(condition, message) do { } while (false)
|
||||
|
||||
// Tell the compiler that this part of the code will never be reached.
|
||||
#if defined( _MSC_VER )
|
||||
|
||||
@ -758,18 +758,21 @@ static void readNumber(Parser* parser)
|
||||
nextChar(parser);
|
||||
while (isDigit(peekChar(parser))) nextChar(parser);
|
||||
}
|
||||
|
||||
|
||||
// See if the number is in scientific notation.
|
||||
if (matchChar(parser, 'e') || matchChar(parser, 'E'))
|
||||
{
|
||||
// Allow a negative exponent.
|
||||
matchChar(parser, '-');
|
||||
|
||||
// Allow a single positive/negative exponent symbol.
|
||||
if(!matchChar(parser, '+'))
|
||||
{
|
||||
matchChar(parser, '-');
|
||||
}
|
||||
|
||||
if (!isDigit(peekChar(parser)))
|
||||
{
|
||||
lexError(parser, "Unterminated scientific notation.");
|
||||
}
|
||||
|
||||
|
||||
while (isDigit(peekChar(parser))) nextChar(parser);
|
||||
}
|
||||
|
||||
@ -2088,7 +2091,7 @@ static void field(Compiler* compiler, bool canAssign)
|
||||
{
|
||||
// Initialize it with a fake value so we can keep parsing and minimize the
|
||||
// number of cascaded errors.
|
||||
int field = 255;
|
||||
int field = MAX_FIELDS;
|
||||
|
||||
ClassInfo* enclosingClass = getEnclosingClass(compiler);
|
||||
|
||||
@ -2692,8 +2695,8 @@ void expression(Compiler* compiler)
|
||||
|
||||
// Returns the number of arguments to the instruction at [ip] in [fn]'s
|
||||
// bytecode.
|
||||
static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
int ip)
|
||||
static int getByteCountForArguments(const uint8_t* bytecode,
|
||||
const Value* constants, int ip)
|
||||
{
|
||||
Code instruction = (Code)bytecode[ip];
|
||||
switch (instruction)
|
||||
@ -2759,6 +2762,7 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
case CODE_METHOD_INSTANCE:
|
||||
case CODE_METHOD_STATIC:
|
||||
case CODE_IMPORT_MODULE:
|
||||
case CODE_IMPORT_VARIABLE:
|
||||
return 2;
|
||||
|
||||
case CODE_SUPER_0:
|
||||
@ -2778,7 +2782,6 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
case CODE_SUPER_14:
|
||||
case CODE_SUPER_15:
|
||||
case CODE_SUPER_16:
|
||||
case CODE_IMPORT_VARIABLE:
|
||||
return 4;
|
||||
|
||||
case CODE_CLOSURE:
|
||||
@ -2846,7 +2849,7 @@ static void endLoop(Compiler* compiler)
|
||||
else
|
||||
{
|
||||
// Skip this instruction and its arguments.
|
||||
i += 1 + getNumArguments(compiler->fn->code.data,
|
||||
i += 1 + getByteCountForArguments(compiler->fn->code.data,
|
||||
compiler->fn->constants.data, i);
|
||||
}
|
||||
}
|
||||
@ -3563,7 +3566,7 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
|
||||
// Other instructions are unaffected, so just skip over them.
|
||||
break;
|
||||
}
|
||||
ip += 1 + getNumArguments(fn->code.data, fn->constants.data, ip);
|
||||
ip += 1 + getByteCountForArguments(fn->code.data, fn->constants.data, ip);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -259,12 +259,12 @@ static void call_fn(WrenVM* vm, Value* args, int numArgs)
|
||||
wrenCallFunction(vm, vm->fiber, AS_CLOSURE(args[0]), numArgs + 1);
|
||||
}
|
||||
|
||||
#define DEF_FN_CALL(numArgs) \
|
||||
DEF_PRIMITIVE(fn_call##numArgs) \
|
||||
{ \
|
||||
call_fn(vm, args, numArgs); \
|
||||
return false; \
|
||||
} \
|
||||
#define DEF_FN_CALL(numArgs) \
|
||||
DEF_PRIMITIVE(fn_call##numArgs) \
|
||||
{ \
|
||||
call_fn(vm, args, numArgs); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
DEF_FN_CALL(0)
|
||||
DEF_FN_CALL(1)
|
||||
@ -600,11 +600,11 @@ DEF_PRIMITIVE(num_pi)
|
||||
}
|
||||
|
||||
// Defines a primitive on Num that calls infix [op] and returns [type].
|
||||
#define DEF_NUM_INFIX(name, op, type) \
|
||||
DEF_PRIMITIVE(num_##name) \
|
||||
{ \
|
||||
if (!validateNum(vm, args[1], "Right operand")) return false; \
|
||||
RETURN_##type(AS_NUM(args[0]) op AS_NUM(args[1])); \
|
||||
#define DEF_NUM_INFIX(name, op, type) \
|
||||
DEF_PRIMITIVE(num_##name) \
|
||||
{ \
|
||||
if (!validateNum(vm, args[1], "Right operand")) return false; \
|
||||
RETURN_##type(AS_NUM(args[0]) op AS_NUM(args[1])); \
|
||||
}
|
||||
|
||||
DEF_NUM_INFIX(minus, -, NUM)
|
||||
@ -617,13 +617,13 @@ DEF_NUM_INFIX(lte, <=, BOOL)
|
||||
DEF_NUM_INFIX(gte, >=, BOOL)
|
||||
|
||||
// Defines a primitive on Num that call infix bitwise [op].
|
||||
#define DEF_NUM_BITWISE(name, op) \
|
||||
DEF_PRIMITIVE(num_bitwise##name) \
|
||||
{ \
|
||||
if (!validateNum(vm, args[1], "Right operand")) return false; \
|
||||
uint32_t left = (uint32_t)AS_NUM(args[0]); \
|
||||
uint32_t right = (uint32_t)AS_NUM(args[1]); \
|
||||
RETURN_NUM(left op right); \
|
||||
#define DEF_NUM_BITWISE(name, op) \
|
||||
DEF_PRIMITIVE(num_bitwise##name) \
|
||||
{ \
|
||||
if (!validateNum(vm, args[1], "Right operand")) return false; \
|
||||
uint32_t left = (uint32_t)AS_NUM(args[0]); \
|
||||
uint32_t right = (uint32_t)AS_NUM(args[1]); \
|
||||
RETURN_NUM(left op right); \
|
||||
}
|
||||
|
||||
DEF_NUM_BITWISE(And, &)
|
||||
@ -633,10 +633,10 @@ DEF_NUM_BITWISE(LeftShift, <<)
|
||||
DEF_NUM_BITWISE(RightShift, >>)
|
||||
|
||||
// Defines a primitive method on Num that returns the result of [fn].
|
||||
#define DEF_NUM_FN(name, fn) \
|
||||
DEF_PRIMITIVE(num_##name) \
|
||||
{ \
|
||||
RETURN_NUM(fn(AS_NUM(args[0]))); \
|
||||
#define DEF_NUM_FN(name, fn) \
|
||||
DEF_PRIMITIVE(num_##name) \
|
||||
{ \
|
||||
RETURN_NUM(fn(AS_NUM(args[0]))); \
|
||||
}
|
||||
|
||||
DEF_NUM_FN(abs, fabs)
|
||||
@ -652,6 +652,8 @@ DEF_NUM_FN(sin, sin)
|
||||
DEF_NUM_FN(sqrt, sqrt)
|
||||
DEF_NUM_FN(tan, tan)
|
||||
DEF_NUM_FN(log, log)
|
||||
DEF_NUM_FN(log2, log2)
|
||||
DEF_NUM_FN(exp, exp)
|
||||
|
||||
DEF_PRIMITIVE(num_mod)
|
||||
{
|
||||
@ -1245,23 +1247,25 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->fnClass->obj.classObj, "new(_)", fn_new);
|
||||
|
||||
PRIMITIVE(vm->fnClass, "arity", fn_arity);
|
||||
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);
|
||||
|
||||
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, "toString", fn_toString);
|
||||
|
||||
vm->nullClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Null"));
|
||||
@ -1299,6 +1303,8 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->numClass, "sqrt", num_sqrt);
|
||||
PRIMITIVE(vm->numClass, "tan", num_tan);
|
||||
PRIMITIVE(vm->numClass, "log", num_log);
|
||||
PRIMITIVE(vm->numClass, "log2", num_log2);
|
||||
PRIMITIVE(vm->numClass, "exp", num_exp);
|
||||
PRIMITIVE(vm->numClass, "%(_)", num_mod);
|
||||
PRIMITIVE(vm->numClass, "~", num_bitwiseNot);
|
||||
PRIMITIVE(vm->numClass, "..(_)", num_dotDot);
|
||||
|
||||
@ -121,9 +121,9 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
#define READ_BYTE() (bytecode[i++])
|
||||
#define READ_SHORT() (i += 2, (bytecode[i - 2] << 8) | bytecode[i - 1])
|
||||
|
||||
#define BYTE_INSTRUCTION(name) \
|
||||
printf("%-16s %5d\n", name, READ_BYTE()); \
|
||||
break; \
|
||||
#define BYTE_INSTRUCTION(name) \
|
||||
printf("%-16s %5d\n", name, READ_BYTE()); \
|
||||
break
|
||||
|
||||
switch (code)
|
||||
{
|
||||
|
||||
@ -23,9 +23,7 @@ static uint32_t validateIndexValue(WrenVM* vm, uint32_t count, double value,
|
||||
bool validateFn(WrenVM* vm, Value arg, const char* argName)
|
||||
{
|
||||
if (IS_CLOSURE(arg)) return true;
|
||||
|
||||
vm->fiber->error = wrenStringFormat(vm, "$ must be a function.", argName);
|
||||
return false;
|
||||
RETURN_ERROR_FMT("$ must be a function.", argName);
|
||||
}
|
||||
|
||||
bool validateNum(WrenVM* vm, Value arg, const char* argName)
|
||||
|
||||
@ -5,23 +5,42 @@
|
||||
|
||||
// Binds a primitive method named [name] (in Wren) implemented using C function
|
||||
// [fn] to `ObjClass` [cls].
|
||||
#define PRIMITIVE(cls, name, function) \
|
||||
{ \
|
||||
int symbol = wrenSymbolTableEnsure(vm, \
|
||||
&vm->methodNames, name, strlen(name)); \
|
||||
Method method; \
|
||||
method.type = METHOD_PRIMITIVE; \
|
||||
method.as.primitive = prim_##function; \
|
||||
wrenBindMethod(vm, cls, symbol, method); \
|
||||
}
|
||||
#define PRIMITIVE(cls, name, function) \
|
||||
do \
|
||||
{ \
|
||||
int symbol = wrenSymbolTableEnsure(vm, \
|
||||
&vm->methodNames, name, strlen(name)); \
|
||||
Method method; \
|
||||
method.type = METHOD_PRIMITIVE; \
|
||||
method.as.primitive = prim_##function; \
|
||||
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.
|
||||
#define DEF_PRIMITIVE(name) \
|
||||
#define DEF_PRIMITIVE(name) \
|
||||
static bool prim_##name(WrenVM* vm, Value* args)
|
||||
|
||||
#define RETURN_VAL(value) do { args[0] = value; return true; } while (0)
|
||||
#define RETURN_VAL(value) \
|
||||
do \
|
||||
{ \
|
||||
args[0] = value; \
|
||||
return true; \
|
||||
} while (false)
|
||||
|
||||
#define RETURN_OBJ(obj) RETURN_VAL(OBJ_VAL(obj))
|
||||
#define RETURN_BOOL(value) RETURN_VAL(BOOL_VAL(value))
|
||||
@ -30,17 +49,19 @@
|
||||
#define RETURN_NUM(value) RETURN_VAL(NUM_VAL(value))
|
||||
#define RETURN_TRUE RETURN_VAL(TRUE_VAL)
|
||||
|
||||
#define RETURN_ERROR(msg) \
|
||||
do { \
|
||||
vm->fiber->error = wrenNewStringLength(vm, msg, sizeof(msg) - 1); \
|
||||
return false; \
|
||||
} while (0);
|
||||
#define RETURN_ERROR(msg) \
|
||||
do \
|
||||
{ \
|
||||
vm->fiber->error = wrenNewStringLength(vm, msg, sizeof(msg) - 1); \
|
||||
return false; \
|
||||
} while (false)
|
||||
|
||||
#define RETURN_ERROR_FMT(msg, arg) \
|
||||
do { \
|
||||
vm->fiber->error = wrenStringFormat(vm, msg, arg); \
|
||||
return false; \
|
||||
} while (0);
|
||||
#define RETURN_ERROR_FMT(...) \
|
||||
do \
|
||||
{ \
|
||||
vm->fiber->error = wrenStringFormat(vm, __VA_ARGS__); \
|
||||
return false; \
|
||||
} while (false)
|
||||
|
||||
// Validates that the given [arg] is a function. Returns true if it is. If not,
|
||||
// reports an error and returns false.
|
||||
|
||||
@ -13,54 +13,54 @@ typedef struct sObjString ObjString;
|
||||
// We need buffers of a few different types. To avoid lots of casting between
|
||||
// void* and back, we'll use the preprocessor as a poor man's generics and let
|
||||
// it generate a few type-specific ones.
|
||||
#define DECLARE_BUFFER(name, type) \
|
||||
typedef struct \
|
||||
{ \
|
||||
type* data; \
|
||||
int count; \
|
||||
int capacity; \
|
||||
} name##Buffer; \
|
||||
void wren##name##BufferInit(name##Buffer* buffer); \
|
||||
void wren##name##BufferClear(WrenVM* vm, name##Buffer* buffer); \
|
||||
void wren##name##BufferFill(WrenVM* vm, name##Buffer* buffer, type data, \
|
||||
int count); \
|
||||
#define DECLARE_BUFFER(name, type) \
|
||||
typedef struct \
|
||||
{ \
|
||||
type* data; \
|
||||
int count; \
|
||||
int capacity; \
|
||||
} name##Buffer; \
|
||||
void wren##name##BufferInit(name##Buffer* buffer); \
|
||||
void wren##name##BufferClear(WrenVM* vm, name##Buffer* buffer); \
|
||||
void wren##name##BufferFill(WrenVM* vm, name##Buffer* buffer, type data, \
|
||||
int count); \
|
||||
void wren##name##BufferWrite(WrenVM* vm, name##Buffer* buffer, type data)
|
||||
|
||||
// This should be used once for each type instantiation, somewhere in a .c file.
|
||||
#define DEFINE_BUFFER(name, type) \
|
||||
void wren##name##BufferInit(name##Buffer* buffer) \
|
||||
{ \
|
||||
buffer->data = NULL; \
|
||||
buffer->capacity = 0; \
|
||||
buffer->count = 0; \
|
||||
} \
|
||||
\
|
||||
void wren##name##BufferClear(WrenVM* vm, name##Buffer* buffer) \
|
||||
{ \
|
||||
wrenReallocate(vm, buffer->data, 0, 0); \
|
||||
wren##name##BufferInit(buffer); \
|
||||
} \
|
||||
\
|
||||
void wren##name##BufferFill(WrenVM* vm, name##Buffer* buffer, type data, \
|
||||
int count) \
|
||||
{ \
|
||||
if (buffer->capacity < buffer->count + count) \
|
||||
{ \
|
||||
int capacity = wrenPowerOf2Ceil(buffer->count + count); \
|
||||
buffer->data = (type*)wrenReallocate(vm, buffer->data, \
|
||||
buffer->capacity * sizeof(type), capacity * sizeof(type)); \
|
||||
buffer->capacity = capacity; \
|
||||
} \
|
||||
\
|
||||
for (int i = 0; i < count; i++) \
|
||||
{ \
|
||||
buffer->data[buffer->count++] = data; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void wren##name##BufferWrite(WrenVM* vm, name##Buffer* buffer, type data) \
|
||||
{ \
|
||||
wren##name##BufferFill(vm, buffer, data, 1); \
|
||||
#define DEFINE_BUFFER(name, type) \
|
||||
void wren##name##BufferInit(name##Buffer* buffer) \
|
||||
{ \
|
||||
buffer->data = NULL; \
|
||||
buffer->capacity = 0; \
|
||||
buffer->count = 0; \
|
||||
} \
|
||||
\
|
||||
void wren##name##BufferClear(WrenVM* vm, name##Buffer* buffer) \
|
||||
{ \
|
||||
wrenReallocate(vm, buffer->data, 0, 0); \
|
||||
wren##name##BufferInit(buffer); \
|
||||
} \
|
||||
\
|
||||
void wren##name##BufferFill(WrenVM* vm, name##Buffer* buffer, type data, \
|
||||
int count) \
|
||||
{ \
|
||||
if (buffer->capacity < buffer->count + count) \
|
||||
{ \
|
||||
int capacity = wrenPowerOf2Ceil(buffer->count + count); \
|
||||
buffer->data = (type*)wrenReallocate(vm, buffer->data, \
|
||||
buffer->capacity * sizeof(type), capacity * sizeof(type)); \
|
||||
buffer->capacity = capacity; \
|
||||
} \
|
||||
\
|
||||
for (int i = 0; i < count; i++) \
|
||||
{ \
|
||||
buffer->data[buffer->count++] = data; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void wren##name##BufferWrite(WrenVM* vm, name##Buffer* buffer, type data) \
|
||||
{ \
|
||||
wren##name##BufferFill(vm, buffer, data, 1); \
|
||||
}
|
||||
|
||||
DECLARE_BUFFER(Byte, uint8_t);
|
||||
|
||||
@ -76,6 +76,7 @@
|
||||
#define IS_FOREIGN(value) (wrenIsObjType(value, OBJ_FOREIGN)) // ObjForeign
|
||||
#define IS_INSTANCE(value) (wrenIsObjType(value, OBJ_INSTANCE)) // ObjInstance
|
||||
#define IS_LIST(value) (wrenIsObjType(value, OBJ_LIST)) // ObjList
|
||||
#define IS_MAP(value) (wrenIsObjType(value, OBJ_MAP)) // ObjMap
|
||||
#define IS_RANGE(value) (wrenIsObjType(value, OBJ_RANGE)) // ObjRange
|
||||
#define IS_STRING(value) (wrenIsObjType(value, OBJ_STRING)) // ObjString
|
||||
|
||||
@ -182,7 +183,7 @@ typedef struct sObjUpvalue
|
||||
// Pointer to the variable this upvalue is referencing.
|
||||
Value* value;
|
||||
|
||||
// If the upvalue is closed (i.e. the local variable it was pointing too has
|
||||
// If the upvalue is closed (i.e. the local variable it was pointing to has
|
||||
// been popped off the stack) then the closed-over value will be hoisted out
|
||||
// of the stack into here. [value] will then be changed to point to this.
|
||||
Value closed;
|
||||
@ -316,8 +317,8 @@ typedef struct sObjFiber
|
||||
Obj obj;
|
||||
|
||||
// The stack of value slots. This is used for holding local variables and
|
||||
// temporaries while the fiber is executing. It heap-allocated and grown as
|
||||
// needed.
|
||||
// temporaries while the fiber is executing. It is heap-allocated and grown
|
||||
// as needed.
|
||||
Value* stack;
|
||||
|
||||
// A pointer to one past the top-most value on the stack.
|
||||
@ -358,6 +359,9 @@ 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,
|
||||
|
||||
|
||||
171
src/vm/wren_vm.c
171
src/vm/wren_vm.c
@ -6,6 +6,7 @@
|
||||
#include "wren_compiler.h"
|
||||
#include "wren_core.h"
|
||||
#include "wren_debug.h"
|
||||
#include "wren_primitive.h"
|
||||
#include "wren_vm.h"
|
||||
|
||||
#if WREN_OPT_META
|
||||
@ -187,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 %.3fs.\n",
|
||||
printf("GC %lu before, %lu after (%lu collected), next at %lu. Took %.3fms.\n",
|
||||
(unsigned long)before,
|
||||
(unsigned long)vm->bytesAllocated,
|
||||
(unsigned long)(before - vm->bytesAllocated),
|
||||
(unsigned long)vm->nextGC,
|
||||
elapsed);
|
||||
elapsed*1000.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -445,10 +446,17 @@ static ObjClosure* compileInModule(WrenVM* vm, Value name, const char* source,
|
||||
{
|
||||
module = wrenNewModule(vm, AS_STRING(name));
|
||||
|
||||
// It's possible for the wrenMapSet below to resize the modules map,
|
||||
// and trigger a GC while doing so. When this happens it will collect
|
||||
// the module we've just created. Once in the map it is safe.
|
||||
wrenPushRoot(vm, (Obj*)module);
|
||||
|
||||
// Store it in the VM's module registry so we don't load the same module
|
||||
// multiple times.
|
||||
wrenMapSet(vm, vm->modules, name, OBJ_VAL(module));
|
||||
|
||||
wrenPopRoot(vm);
|
||||
|
||||
// Implicitly import the core module.
|
||||
ObjModule* coreModule = getModule(vm, NULL_VAL);
|
||||
for (int i = 0; i < coreModule->variables.count; i++)
|
||||
@ -797,36 +805,37 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
|
||||
// Use this after a CallFrame has been pushed or popped to refresh the local
|
||||
// variables.
|
||||
#define LOAD_FRAME() \
|
||||
frame = &fiber->frames[fiber->numFrames - 1]; \
|
||||
stackStart = frame->stackStart; \
|
||||
ip = frame->ip; \
|
||||
fn = frame->closure->fn;
|
||||
#define LOAD_FRAME() \
|
||||
do \
|
||||
{ \
|
||||
frame = &fiber->frames[fiber->numFrames - 1]; \
|
||||
stackStart = frame->stackStart; \
|
||||
ip = frame->ip; \
|
||||
fn = frame->closure->fn; \
|
||||
} while (false)
|
||||
|
||||
// Terminates the current fiber with error string [error]. If another calling
|
||||
// fiber is willing to catch the error, transfers control to it, otherwise
|
||||
// exits the interpreter.
|
||||
#define RUNTIME_ERROR() \
|
||||
do \
|
||||
{ \
|
||||
STORE_FRAME(); \
|
||||
runtimeError(vm); \
|
||||
if (vm->fiber == NULL) return WREN_RESULT_RUNTIME_ERROR; \
|
||||
fiber = vm->fiber; \
|
||||
LOAD_FRAME(); \
|
||||
DISPATCH(); \
|
||||
} \
|
||||
while (false)
|
||||
#define RUNTIME_ERROR() \
|
||||
do \
|
||||
{ \
|
||||
STORE_FRAME(); \
|
||||
runtimeError(vm); \
|
||||
if (vm->fiber == NULL) return WREN_RESULT_RUNTIME_ERROR; \
|
||||
fiber = vm->fiber; \
|
||||
LOAD_FRAME(); \
|
||||
DISPATCH(); \
|
||||
} while (false)
|
||||
|
||||
#if WREN_DEBUG_TRACE_INSTRUCTIONS
|
||||
// Prints the stack and instruction before each instruction is executed.
|
||||
#define DEBUG_TRACE_INSTRUCTIONS() \
|
||||
do \
|
||||
{ \
|
||||
wrenDumpStack(fiber); \
|
||||
wrenDumpInstruction(vm, fn, (int)(ip - fn->code.data)); \
|
||||
} \
|
||||
while (false)
|
||||
#define DEBUG_TRACE_INSTRUCTIONS() \
|
||||
do \
|
||||
{ \
|
||||
wrenDumpStack(fiber); \
|
||||
wrenDumpInstruction(vm, fn, (int)(ip - fn->code.data)); \
|
||||
} while (false)
|
||||
#else
|
||||
#define DEBUG_TRACE_INSTRUCTIONS() do { } while (false)
|
||||
#endif
|
||||
@ -842,19 +851,18 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
#define INTERPRET_LOOP DISPATCH();
|
||||
#define CASE_CODE(name) code_##name
|
||||
|
||||
#define DISPATCH() \
|
||||
do \
|
||||
{ \
|
||||
DEBUG_TRACE_INSTRUCTIONS(); \
|
||||
goto *dispatchTable[instruction = (Code)READ_BYTE()]; \
|
||||
} \
|
||||
while (false)
|
||||
#define DISPATCH() \
|
||||
do \
|
||||
{ \
|
||||
DEBUG_TRACE_INSTRUCTIONS(); \
|
||||
goto *dispatchTable[instruction = (Code)READ_BYTE()]; \
|
||||
} while (false)
|
||||
|
||||
#else
|
||||
|
||||
#define INTERPRET_LOOP \
|
||||
loop: \
|
||||
DEBUG_TRACE_INSTRUCTIONS(); \
|
||||
#define INTERPRET_LOOP \
|
||||
loop: \
|
||||
DEBUG_TRACE_INSTRUCTIONS(); \
|
||||
switch (instruction = (Code)READ_BYTE())
|
||||
|
||||
#define CASE_CODE(name) case CODE_##name
|
||||
@ -1007,6 +1015,12 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
}
|
||||
break;
|
||||
|
||||
case METHOD_FUNCTION_CALL:
|
||||
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();
|
||||
@ -1446,7 +1460,8 @@ 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);
|
||||
}
|
||||
|
||||
@ -1592,6 +1607,7 @@ WrenType wrenGetSlotType(WrenVM* vm, int slot)
|
||||
if (IS_NUM(vm->apiStack[slot])) return WREN_TYPE_NUM;
|
||||
if (IS_FOREIGN(vm->apiStack[slot])) return WREN_TYPE_FOREIGN;
|
||||
if (IS_LIST(vm->apiStack[slot])) return WREN_TYPE_LIST;
|
||||
if (IS_MAP(vm->apiStack[slot])) return WREN_TYPE_MAP;
|
||||
if (IS_NULL(vm->apiStack[slot])) return WREN_TYPE_NULL;
|
||||
if (IS_STRING(vm->apiStack[slot])) return WREN_TYPE_STRING;
|
||||
|
||||
@ -1690,6 +1706,11 @@ void wrenSetSlotNewList(WrenVM* vm, int slot)
|
||||
setSlot(vm, slot, OBJ_VAL(wrenNewList(vm, 0)));
|
||||
}
|
||||
|
||||
void wrenSetSlotNewMap(WrenVM* vm, int slot)
|
||||
{
|
||||
setSlot(vm, slot, OBJ_VAL(wrenNewMap(vm)));
|
||||
}
|
||||
|
||||
void wrenSetSlotNull(WrenVM* vm, int slot)
|
||||
{
|
||||
setSlot(vm, slot, NULL_VAL);
|
||||
@ -1744,13 +1765,87 @@ void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
|
||||
wrenListInsert(vm, list, vm->apiStack[elementSlot], index);
|
||||
}
|
||||
|
||||
int wrenGetMapCount(WrenVM* vm, int slot)
|
||||
{
|
||||
validateApiSlot(vm, slot);
|
||||
ASSERT(IS_MAP(vm->apiStack[slot]), "Slot must hold a map.");
|
||||
|
||||
ObjMap* map = AS_MAP(vm->apiStack[slot]);
|
||||
return map->count;
|
||||
}
|
||||
|
||||
bool wrenGetMapContainsKey(WrenVM* vm, int mapSlot, int keySlot)
|
||||
{
|
||||
validateApiSlot(vm, mapSlot);
|
||||
validateApiSlot(vm, keySlot);
|
||||
ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Slot must hold a map.");
|
||||
|
||||
Value key = vm->apiStack[keySlot];
|
||||
if (!validateKey(vm, key)) return false;
|
||||
|
||||
ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
|
||||
Value value = wrenMapGet(map, key);
|
||||
|
||||
return !IS_UNDEFINED(value);
|
||||
}
|
||||
|
||||
void wrenGetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot)
|
||||
{
|
||||
validateApiSlot(vm, mapSlot);
|
||||
validateApiSlot(vm, keySlot);
|
||||
validateApiSlot(vm, valueSlot);
|
||||
ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Slot must hold a map.");
|
||||
|
||||
ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
|
||||
Value value = wrenMapGet(map, vm->apiStack[keySlot]);
|
||||
if (IS_UNDEFINED(value)) {
|
||||
value = NULL_VAL;
|
||||
}
|
||||
|
||||
vm->apiStack[valueSlot] = value;
|
||||
}
|
||||
|
||||
void wrenSetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot)
|
||||
{
|
||||
validateApiSlot(vm, mapSlot);
|
||||
validateApiSlot(vm, keySlot);
|
||||
validateApiSlot(vm, valueSlot);
|
||||
ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Must insert into a map.");
|
||||
|
||||
Value key = vm->apiStack[keySlot];
|
||||
if (!validateKey(vm, key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Value value = vm->apiStack[valueSlot];
|
||||
ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
|
||||
|
||||
wrenMapSet(vm, map, key, value);
|
||||
}
|
||||
|
||||
void wrenRemoveMapValue(WrenVM* vm, int mapSlot, int keySlot,
|
||||
int removedValueSlot)
|
||||
{
|
||||
validateApiSlot(vm, mapSlot);
|
||||
validateApiSlot(vm, keySlot);
|
||||
ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Slot must hold a map.");
|
||||
|
||||
Value key = vm->apiStack[keySlot];
|
||||
if (!validateKey(vm, key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
|
||||
Value removed = wrenMapRemoveKey(vm, map, key);
|
||||
setSlot(vm, removedValueSlot, removed);
|
||||
}
|
||||
|
||||
void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
|
||||
int slot)
|
||||
{
|
||||
ASSERT(module != NULL, "Module cannot be NULL.");
|
||||
ASSERT(name != NULL, "Variable name cannot be NULL.");
|
||||
validateApiSlot(vm, slot);
|
||||
|
||||
|
||||
Value moduleName = wrenStringFormat(vm, "$", module);
|
||||
wrenPushRoot(vm, AS_OBJ(moduleName));
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
// The maximum number of temporary objects that can be made visible to the GC
|
||||
// at one time.
|
||||
#define WREN_MAX_TEMP_ROOTS 5
|
||||
#define WREN_MAX_TEMP_ROOTS 8
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
||||
@ -40,6 +40,9 @@ WrenForeignMethodFn APITest_bindForeignMethod(
|
||||
method = listsBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = mapsBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = newVMBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include "foreign_class.h"
|
||||
#include "handle.h"
|
||||
#include "lists.h"
|
||||
#include "maps.h"
|
||||
#include "new_vm.h"
|
||||
#include "reset_stack_after_call_abort.h"
|
||||
#include "reset_stack_after_foreign_construct.h"
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "wren.h"
|
||||
#include "../test.h"
|
||||
|
||||
int callWrenCallRootRunTests(WrenVM* vm)
|
||||
{
|
||||
@ -17,7 +18,7 @@ int callWrenCallRootRunTests(WrenVM* vm)
|
||||
WrenInterpretResult result = wrenCall(vm, run);
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR)
|
||||
{
|
||||
exitCode = 70;
|
||||
exitCode = WREN_EX_SOFTWARE;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
130
test/api/maps.c
Normal file
130
test/api/maps.c
Normal file
@ -0,0 +1,130 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "maps.h"
|
||||
|
||||
static void newMap(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotNewMap(vm, 0);
|
||||
}
|
||||
|
||||
static void invalidInsert(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotNewMap(vm, 0);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
// Foreign Class is in slot 1
|
||||
wrenSetSlotString(vm, 2, "England");
|
||||
wrenSetMapValue(vm, 0, 1, 2); // expect this to cause errors
|
||||
}
|
||||
|
||||
static void insert(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotNewMap(vm, 0);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
|
||||
// Insert String
|
||||
wrenSetSlotString(vm, 1, "England");
|
||||
wrenSetSlotString(vm, 2, "London");
|
||||
wrenSetMapValue(vm, 0, 1, 2);
|
||||
|
||||
// Insert Double
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenSetSlotDouble(vm, 2, 42.0);
|
||||
wrenSetMapValue(vm, 0, 1, 2);
|
||||
|
||||
// Insert Boolean
|
||||
wrenSetSlotBool(vm, 1, false);
|
||||
wrenSetSlotBool(vm, 2, true);
|
||||
wrenSetMapValue(vm, 0, 1, 2);
|
||||
|
||||
// Insert Null
|
||||
wrenSetSlotNull(vm, 1);
|
||||
wrenSetSlotNull(vm, 2);
|
||||
wrenSetMapValue(vm, 0, 1, 2);
|
||||
|
||||
// Insert List
|
||||
wrenSetSlotString(vm, 1, "Empty");
|
||||
wrenSetSlotNewList(vm, 2);
|
||||
wrenSetMapValue(vm, 0, 1, 2);
|
||||
}
|
||||
|
||||
static void remove(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 3);
|
||||
|
||||
wrenSetSlotString(vm, 2, "key");
|
||||
wrenRemoveMapValue(vm, 1, 2, 0);
|
||||
}
|
||||
|
||||
static void countWren(WrenVM* vm)
|
||||
{
|
||||
int count = wrenGetMapCount(vm, 1);
|
||||
wrenSetSlotDouble(vm, 0, count);
|
||||
}
|
||||
|
||||
static void countAPI(WrenVM* vm)
|
||||
{
|
||||
insert(vm);
|
||||
int count = wrenGetMapCount(vm, 0);
|
||||
wrenSetSlotDouble(vm, 0, count);
|
||||
}
|
||||
|
||||
static void containsWren(WrenVM* vm)
|
||||
{
|
||||
bool result = wrenGetMapContainsKey(vm, 1, 2);
|
||||
wrenSetSlotBool(vm, 0, result);
|
||||
}
|
||||
|
||||
|
||||
static void containsAPI(WrenVM* vm)
|
||||
{
|
||||
insert(vm);
|
||||
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotString(vm, 1, "England");
|
||||
|
||||
bool result = wrenGetMapContainsKey(vm, 0, 1);
|
||||
wrenSetSlotBool(vm, 0, result);
|
||||
}
|
||||
|
||||
static void containsAPIFalse(WrenVM* vm)
|
||||
{
|
||||
insert(vm);
|
||||
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotString(vm, 1, "DefinitelyNotARealKey");
|
||||
|
||||
bool result = wrenGetMapContainsKey(vm, 0, 1);
|
||||
wrenSetSlotBool(vm, 0, result);
|
||||
}
|
||||
|
||||
|
||||
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 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;
|
||||
if (strcmp(signature, "static Maps.containsFalse()") == 0) return containsAPIFalse;
|
||||
if (strcmp(signature, "static Maps.contains(_,_)") == 0) return containsWren;
|
||||
if (strcmp(signature, "static Maps.invalidInsert(_)") == 0) return invalidInsert;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void foreignAllocate(WrenVM* vm) {
|
||||
wrenSetSlotNewForeign(vm, 0, 0, 0);
|
||||
}
|
||||
|
||||
void mapBindClass(
|
||||
const char* className, WrenForeignClassMethods* methods)
|
||||
{
|
||||
if (strcmp(className, "ForeignClass") == 0)
|
||||
{
|
||||
methods->allocate = foreignAllocate;
|
||||
return;
|
||||
}
|
||||
}
|
||||
5
test/api/maps.h
Normal file
5
test/api/maps.h
Normal file
@ -0,0 +1,5 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn mapsBindMethod(const char* signature);
|
||||
void mapBindClass(
|
||||
const char* className, WrenForeignClassMethods* methods);
|
||||
68
test/api/maps.wren
Normal file
68
test/api/maps.wren
Normal file
@ -0,0 +1,68 @@
|
||||
class ForeignClass {
|
||||
construct new() {}
|
||||
}
|
||||
|
||||
class Maps {
|
||||
foreign static newMap()
|
||||
foreign static insert()
|
||||
foreign static contains(map, key)
|
||||
foreign static contains()
|
||||
foreign static containsFalse()
|
||||
foreign static count()
|
||||
foreign static count(map)
|
||||
foreign static remove(map)
|
||||
foreign static invalidInsert(obj)
|
||||
}
|
||||
|
||||
// map new + get/set API
|
||||
|
||||
var map = Maps.newMap()
|
||||
System.print(map is Map) // expect: true
|
||||
System.print(map.count) // expect: 0
|
||||
|
||||
var data = Maps.insert()
|
||||
System.print(data["England"]) // expect: London
|
||||
System.print(data["Empty"]) // expect: []
|
||||
System.print(data[1.0]) // expect: 42
|
||||
System.print(data[false]) // expect: true
|
||||
System.print(data[null]) // expect: null
|
||||
|
||||
// remove API
|
||||
|
||||
var removed = Maps.remove({ "key":"value", "other":"data" })
|
||||
System.print(removed) // expect: value
|
||||
|
||||
var removedNone = Maps.remove({})
|
||||
System.print(removedNone) // expect: null
|
||||
|
||||
// count API
|
||||
|
||||
var countMap = { "key":"value", "other":"data", 4:"number key" }
|
||||
System.print(Maps.count(countMap)) // expect: 3
|
||||
Maps.remove(countMap) //remove using API
|
||||
System.print(Maps.count(countMap)) // expect: 2
|
||||
countMap.remove("other") //remove wren side
|
||||
System.print(Maps.count(countMap)) // expect: 1
|
||||
|
||||
var countAPI = Maps.count()
|
||||
System.print(countAPI) // expect: 5
|
||||
|
||||
//contains key API
|
||||
|
||||
var containsMap = { "key":"value", "other":"data", 4:"number key" }
|
||||
System.print(Maps.contains(containsMap, "key")) // expect: true
|
||||
System.print(Maps.contains(containsMap, "fake")) // expect: false
|
||||
System.print(Maps.contains(containsMap, "other")) // expect: true
|
||||
|
||||
Maps.remove(containsMap) //remove using API
|
||||
System.print(Maps.contains(containsMap, "key")) // expect: false
|
||||
|
||||
containsMap.remove("other") //remove wren side
|
||||
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.
|
||||
@ -13,9 +13,47 @@ static void nullConfig(WrenVM* vm)
|
||||
wrenFreeVM(otherVM);
|
||||
}
|
||||
|
||||
static void multipleInterpretCalls(WrenVM* vm)
|
||||
{
|
||||
WrenVM* otherVM = wrenNewVM(NULL);
|
||||
WrenInterpretResult result;
|
||||
|
||||
bool correct = true;
|
||||
|
||||
// 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)");
|
||||
correct = correct && (result == WREN_RESULT_SUCCESS);
|
||||
|
||||
wrenEnsureSlots(otherVM, 2);
|
||||
wrenSetSlotDouble(otherVM, 0, -i);
|
||||
result = wrenCall(otherVM, absMethod);
|
||||
correct = correct && (result == WREN_RESULT_SUCCESS);
|
||||
|
||||
double absValue = wrenGetSlotDouble(otherVM, 0);
|
||||
correct = correct && (absValue == (double)i);
|
||||
}
|
||||
|
||||
wrenSetSlotBool(vm, 0, correct);
|
||||
|
||||
wrenReleaseHandle(otherVM, absMethod);
|
||||
wrenFreeVM(otherVM);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn newVMBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static VM.nullConfig()") == 0) return nullConfig;
|
||||
if (strcmp(signature, "static VM.multipleInterpretCalls()") == 0) return multipleInterpretCalls;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
class VM {
|
||||
foreign static nullConfig()
|
||||
foreign static multipleInterpretCalls()
|
||||
}
|
||||
// TODO: Other configuration settings.
|
||||
|
||||
System.print(VM.nullConfig()) // expect: true
|
||||
System.print(VM.multipleInterpretCalls()) // expect: true
|
||||
|
||||
@ -82,10 +82,11 @@ static void slotTypes(WrenVM* vm)
|
||||
wrenGetSlotType(vm, 1) == WREN_TYPE_BOOL &&
|
||||
wrenGetSlotType(vm, 2) == WREN_TYPE_FOREIGN &&
|
||||
wrenGetSlotType(vm, 3) == WREN_TYPE_LIST &&
|
||||
wrenGetSlotType(vm, 4) == WREN_TYPE_NULL &&
|
||||
wrenGetSlotType(vm, 5) == WREN_TYPE_NUM &&
|
||||
wrenGetSlotType(vm, 6) == WREN_TYPE_STRING &&
|
||||
wrenGetSlotType(vm, 7) == WREN_TYPE_UNKNOWN;
|
||||
wrenGetSlotType(vm, 4) == WREN_TYPE_MAP &&
|
||||
wrenGetSlotType(vm, 5) == WREN_TYPE_NULL &&
|
||||
wrenGetSlotType(vm, 6) == WREN_TYPE_NUM &&
|
||||
wrenGetSlotType(vm, 7) == WREN_TYPE_STRING &&
|
||||
wrenGetSlotType(vm, 8) == WREN_TYPE_UNKNOWN;
|
||||
|
||||
wrenSetSlotBool(vm, 0, result);
|
||||
}
|
||||
@ -166,16 +167,22 @@ static void getListElement(WrenVM* vm)
|
||||
wrenGetListElement(vm, 1, index, 0);
|
||||
}
|
||||
|
||||
static void getMapValue(WrenVM* vm)
|
||||
{
|
||||
wrenGetMapValue(vm, 1, 2, 0);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn slotsBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Slots.noSet") == 0) return noSet;
|
||||
if (strcmp(signature, "static Slots.getSlots(_,_,_,_,_)") == 0) return getSlots;
|
||||
if (strcmp(signature, "static Slots.setSlots(_,_,_,_,_)") == 0) return setSlots;
|
||||
if (strcmp(signature, "static Slots.slotTypes(_,_,_,_,_,_,_)") == 0) return slotTypes;
|
||||
if (strcmp(signature, "static Slots.slotTypes(_,_,_,_,_,_,_,_)") == 0) return slotTypes;
|
||||
if (strcmp(signature, "static Slots.ensure()") == 0) return ensure;
|
||||
if (strcmp(signature, "static Slots.ensureOutsideForeign()") == 0) return ensureOutsideForeign;
|
||||
if (strcmp(signature, "static Slots.getListCount(_)") == 0) return getListCount;
|
||||
if (strcmp(signature, "static Slots.getListElement(_,_)") == 0) return getListElement;
|
||||
if (strcmp(signature, "static Slots.getMapValue(_,_)") == 0) return getMapValue;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -2,11 +2,12 @@ class Slots {
|
||||
foreign static noSet
|
||||
foreign static getSlots(bool, num, string, bytes, value)
|
||||
foreign static setSlots(a, b, c, d, e)
|
||||
foreign static slotTypes(bool, foreignObj, list, nullObj, num, string, unknown)
|
||||
foreign static slotTypes(bool, foreignObj, list, map, nullObj, num, string, unknown)
|
||||
foreign static ensure()
|
||||
foreign static ensureOutsideForeign()
|
||||
foreign static getListCount(list)
|
||||
foreign static getListElement(list, index)
|
||||
foreign static getMapValue(map, key)
|
||||
}
|
||||
|
||||
foreign class ForeignType {
|
||||
@ -24,7 +25,7 @@ System.print(Slots.getSlots(true, "by\0te", 1.5, "str", value) == value)
|
||||
System.print(Slots.setSlots(value, 0, 0, 0, 0) == value)
|
||||
// expect: true
|
||||
|
||||
System.print(Slots.slotTypes(false, ForeignType.new(), [], null, 1.2, "str", 1..2))
|
||||
System.print(Slots.slotTypes(false, ForeignType.new(), [], {}, null, 1.2, "str", 1..2))
|
||||
// expect: true
|
||||
|
||||
System.print(Slots.ensure())
|
||||
@ -37,3 +38,14 @@ var ducks = ["Huey", "Dewey", "Louie"]
|
||||
System.print(Slots.getListCount(ducks)) // expect: 3
|
||||
System.print(Slots.getListElement(ducks, 0)) // expect: Huey
|
||||
System.print(Slots.getListElement(ducks, 1)) // expect: Dewey
|
||||
|
||||
var capitals = {
|
||||
"England": "London",
|
||||
"Scotland": "Edinburgh",
|
||||
"Wales": "Cardiff",
|
||||
"N. Ireland": "Belfast"
|
||||
}
|
||||
|
||||
System.print(Slots.getMapValue(capitals, "England")) // expect: London
|
||||
System.print(Slots.getMapValue(capitals, "Wales")) // expect: Cardiff
|
||||
System.print(Slots.getMapValue(capitals, "S. Ireland")) // expect: null
|
||||
|
||||
3
test/core/number/exp.wren
Normal file
3
test/core/number/exp.wren
Normal file
@ -0,0 +1,3 @@
|
||||
System.print(5.exp) // expect: 148.41315910258
|
||||
System.print(10.exp) // expect: 22026.465794807
|
||||
System.print((-1).exp) // expect: 0.36787944117144
|
||||
4
test/core/number/log2.wren
Normal file
4
test/core/number/log2.wren
Normal file
@ -0,0 +1,4 @@
|
||||
System.print(1024.log2) // expect: 10
|
||||
System.print(2048.log2) // expect: 11
|
||||
System.print(100.log2) // expect: 6.6438561897747
|
||||
System.print((-1).log2) // expect: nan
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this +(value) { // expect error
|
||||
construct +(value) { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this -(value) { // expect error
|
||||
construct -(value) { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this name=(value) { // expect error
|
||||
construct name=(value) { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this [value] { // expect error
|
||||
construct [value] { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this ! { // expect error
|
||||
construct ! { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this new { // expect error
|
||||
construct new { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,3 +7,4 @@ System.print(2.55e-2 is Num) // expect: true
|
||||
System.print(x is Num) // expect: true
|
||||
System.print(-2.55e2) // expect: -255
|
||||
System.print(-25500e-2) // expect: -255
|
||||
System.print(2.55e+2) // expect: 255
|
||||
|
||||
@ -0,0 +1 @@
|
||||
var x = 1e+-01 // expect error
|
||||
@ -1 +1 @@
|
||||
var f = new Fn {|a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q| "!" } // expect error
|
||||
var f = Fn.new {|a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q| "!" } // expect error
|
||||
|
||||
@ -47,8 +47,8 @@ int main(int argc, const char* argv[]) {
|
||||
exitCode = APITest_Run(vm, testName);
|
||||
}
|
||||
|
||||
if (result == WREN_RESULT_COMPILE_ERROR) return 65; // EX_DATAERR.
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR) return 70; // EX_SOFTWARE.
|
||||
if (result == WREN_RESULT_COMPILE_ERROR) return WREN_EX_DATAERR;
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR) return WREN_EX_SOFTWARE;
|
||||
|
||||
wrenFreeVM(vm);
|
||||
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
import "random" for Random
|
||||
|
||||
var random = Random.new(12345)
|
||||
|
||||
// Should choose all elements with roughly equal probability.
|
||||
var list = ["a", "b", "c"]
|
||||
var histogram = {}
|
||||
for (i in 1..5000) {
|
||||
var sample = random.sample(list, 3)
|
||||
var string = sample.toString
|
||||
if (!histogram.containsKey(string)) histogram[string] = 0
|
||||
histogram[string] = histogram[string] + 1
|
||||
}
|
||||
|
||||
System.print(histogram.count) // expect: 6
|
||||
for (key in histogram.keys) {
|
||||
var error = (histogram[key] / (5000 / 6) - 1).abs
|
||||
if (error > 0.1) System.print("!!! %(error)")
|
||||
}
|
||||
@ -3,17 +3,25 @@ import "random" for Random
|
||||
var random = Random.new(12345)
|
||||
|
||||
// Should choose all elements with roughly equal probability.
|
||||
var list = ["a", "b", "c", "d"]
|
||||
var histogram = {}
|
||||
for (i in 1..5000) {
|
||||
var sample = random.sample(list, 3)
|
||||
var string = sample.toString
|
||||
if (!histogram.containsKey(string)) histogram[string] = 0
|
||||
histogram[string] = histogram[string] + 1
|
||||
}
|
||||
var list = (0...10).toList
|
||||
var binom = [1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
|
||||
|
||||
System.print(histogram.count) // expect: 24
|
||||
for (key in histogram.keys) {
|
||||
var error = (histogram[key] / (5000 / 24) - 1).abs
|
||||
if (error > 0.2) System.print("!!! %(error)")
|
||||
for (k in 0..10) {
|
||||
var count = binom[k]
|
||||
|
||||
var histogram = {}
|
||||
for (i in 1..count * 100) {
|
||||
var sample = random.sample(list, k)
|
||||
// Create a bitmask to represent the unordered set.
|
||||
var bitmask = 0
|
||||
sample.each {|s| bitmask = bitmask | (1 << s) }
|
||||
if (!histogram.containsKey(bitmask)) histogram[bitmask] = 0
|
||||
histogram[bitmask] = histogram[bitmask] + 1
|
||||
}
|
||||
|
||||
if (histogram.count != count) System.print("!!! %(count) %(histogram.count)")
|
||||
for (key in histogram.keys) {
|
||||
var error = (histogram[key] - 100).abs
|
||||
if (error > 50) System.print("!!! %(error)")
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,7 +320,7 @@
|
||||
if (buffer == NULL)
|
||||
{
|
||||
fprintf(stderr, "Could not read file \"%s\".\n", path);
|
||||
exit(74);
|
||||
exit(WREN_EX_IOERR);
|
||||
}
|
||||
|
||||
// Read the entire file.
|
||||
@ -328,7 +328,7 @@
|
||||
if (bytesRead < fileSize)
|
||||
{
|
||||
fprintf(stderr, "Could not read file \"%s\".\n", path);
|
||||
exit(74);
|
||||
exit(WREN_EX_IOERR);
|
||||
}
|
||||
|
||||
// Terminate the string.
|
||||
@ -421,7 +421,7 @@
|
||||
if (source == NULL)
|
||||
{
|
||||
fprintf(stderr, "Could not find file \"%s\".\n", path);
|
||||
exit(66);
|
||||
exit(WREN_EX_NOINPUT);
|
||||
}
|
||||
|
||||
// If it looks like a relative path, make it explicitly relative so that we
|
||||
@ -441,6 +441,7 @@
|
||||
WrenInterpretResult result = wrenInterpret(vm, module->chars, source);
|
||||
|
||||
pathFree(module);
|
||||
free(source);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -451,7 +452,7 @@
|
||||
if (argc < 2)
|
||||
{
|
||||
printf("This is a Wren test runner.\nUsage: wren_test [file]\n");
|
||||
return 64; // EX_USAGE.
|
||||
return WREN_EX_USAGE;
|
||||
}
|
||||
|
||||
if (argc == 2 && strcmp(argv[1], "--version") == 0)
|
||||
|
||||
19
test/test.h
19
test/test.h
@ -6,6 +6,23 @@
|
||||
#include <string.h>
|
||||
#include "wren.h"
|
||||
|
||||
// Exit codes used by the wren binaries, following the BSD standard
|
||||
//
|
||||
// The interpreter was used with an incorrect number of arguments
|
||||
#define WREN_EX_USAGE 64
|
||||
|
||||
// Compilation error
|
||||
#define WREN_EX_DATAERR 65
|
||||
|
||||
// Runtime error
|
||||
#define WREN_EX_SOFTWARE 70
|
||||
|
||||
// Cannot open input file
|
||||
#define WREN_EX_NOINPUT 66
|
||||
|
||||
// I/O Error
|
||||
#define WREN_EX_IOERR 74
|
||||
|
||||
// The maximum number of components in a path. We can't normalize a path that
|
||||
// contains more than this number of parts. The number here assumes a max path
|
||||
// length of 4096, which is common on Linux, and then assumes each component is
|
||||
@ -69,4 +86,4 @@ typedef struct
|
||||
WrenInterpretResult runFile(WrenVM* vm, const char* path);
|
||||
int handle_args(int argc, const char* argv[]);
|
||||
|
||||
#endif //WREN_TEST_H
|
||||
#endif //WREN_TEST_H
|
||||
|
||||
50
util/generate_projects.py
Executable file
50
util/generate_projects.py
Executable file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
from os import getenv, path
|
||||
from subprocess import PIPE, run
|
||||
import platform
|
||||
|
||||
PREMAKE_DIR = path.join(path.dirname(__file__), "../projects/premake")
|
||||
|
||||
# Default binary name
|
||||
PREMAKE_BIN = "premake5"
|
||||
if platform.system() == "Windows":
|
||||
PREMAKE_BIN += ".exe"
|
||||
|
||||
# We try the env first, as that's absolute.
|
||||
# If not found we try the 'intended' approach,
|
||||
# of placing a premake binary alongside premake5.lua.
|
||||
# If that isn't found, attempt the plain binary name.
|
||||
premake = getenv("WREN_PREMAKE", None)
|
||||
if premake is None:
|
||||
premake = PREMAKE_BIN
|
||||
premake_local = path.join(PREMAKE_DIR, PREMAKE_BIN)
|
||||
if path.isfile(premake_local):
|
||||
print("Using local premake in 'projects/premake' ...")
|
||||
premake = premake_local
|
||||
else:
|
||||
print("Using premake from 'WREN_PREMAKE' env ...")
|
||||
|
||||
def run_premake(action, os):
|
||||
run([premake, action, "--os=" + os], cwd=PREMAKE_DIR)
|
||||
|
||||
try:
|
||||
|
||||
run_premake("gmake2", "bsd")
|
||||
run_premake("gmake2", "linux")
|
||||
run_premake("vs2017", "windows")
|
||||
run_premake("vs2019", "windows")
|
||||
run_premake("gmake2", "macosx")
|
||||
run_premake("xcode4", "macosx")
|
||||
|
||||
except Exception as e:
|
||||
|
||||
print("Unable to run premake, while trying the binary '" + premake + "' ...")
|
||||
print(" reason: " + str(e))
|
||||
print("\nIf premake can't be found, possible options are:")
|
||||
print("- Set the env variable 'WREN_PREMAKE' to the path to a premake binary")
|
||||
print("- Place a premake5 binary for your host platform in projects/premake")
|
||||
print("- Add a location with a premake5 binary to the PATH")
|
||||
|
||||
exit(1)
|
||||
19
util/test.py
19
util/test.py
@ -10,7 +10,7 @@ import re
|
||||
from subprocess import Popen, PIPE
|
||||
import sys
|
||||
from threading import Timer
|
||||
|
||||
import platform
|
||||
# Runs the tests.
|
||||
|
||||
parser = ArgumentParser()
|
||||
@ -21,11 +21,19 @@ args = parser.parse_args(sys.argv[1:])
|
||||
|
||||
config = args.suffix.lstrip('_d')
|
||||
is_debug = args.suffix.startswith('_d')
|
||||
config_dir = ("debug" if is_debug else "release") + config
|
||||
|
||||
WREN_DIR = dirname(dirname(realpath(__file__)))
|
||||
WREN_APP = join(WREN_DIR, 'bin', 'wren_test' + args.suffix)
|
||||
|
||||
WREN_APP_WITH_EXT = WREN_APP
|
||||
if platform.system() == "Windows":
|
||||
WREN_APP_WITH_EXT += ".exe"
|
||||
|
||||
if not isfile(WREN_APP_WITH_EXT):
|
||||
print("The binary file 'wren_test' was not found, expected it to be at " + WREN_APP)
|
||||
print("In order to run the tests, you need to build Wren first!")
|
||||
sys.exit(1)
|
||||
|
||||
# print("Wren Test Directory - " + WREN_DIR)
|
||||
# print("Wren Test App - " + WREN_APP)
|
||||
|
||||
@ -45,7 +53,6 @@ num_skipped = 0
|
||||
skipped = defaultdict(int)
|
||||
expectations = 0
|
||||
|
||||
|
||||
class Test:
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
@ -91,7 +98,7 @@ class Test:
|
||||
if match:
|
||||
self.compile_errors.add(line_num)
|
||||
|
||||
# If we expect a compile error, it should exit with EX_DATAERR.
|
||||
# If we expect a compile error, it should exit with WREN_EX_DATAERR.
|
||||
self.exit_code = 65
|
||||
expectations += 1
|
||||
|
||||
@ -99,7 +106,7 @@ class Test:
|
||||
if match:
|
||||
self.compile_errors.add(int(match.group(1)))
|
||||
|
||||
# If we expect a compile error, it should exit with EX_DATAERR.
|
||||
# If we expect a compile error, it should exit with WREN_EX_DATAERR.
|
||||
self.exit_code = 65
|
||||
expectations += 1
|
||||
|
||||
@ -107,7 +114,7 @@ class Test:
|
||||
if match:
|
||||
self.runtime_error_line = line_num
|
||||
self.runtime_error_message = match.group(2)
|
||||
# If the runtime error isn't handled, it should exit with EX_SOFTWARE.
|
||||
# If the runtime error isn't handled, it should exit with WREN_EX_SOFTWARE.
|
||||
if match.group(1) != "handled ":
|
||||
self.exit_code = 70
|
||||
expectations += 1
|
||||
|
||||
Reference in New Issue
Block a user