38 Commits

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

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

This also opens up the option of providing a length parameter or other information needed later (length is optional, and not used as of right now, but exists to show intent).
2020-07-11 11:59:24 -07:00
da091e250c set WREN_MAX_TEMP_ROOTS default to 8 instead of 5
that's 64 bytes, fits nicely in a cache line and isn't _as_ arbitrary.
2020-07-11 11:34:35 -07:00
2ce421eac5 use push root instead of a handle for module GC protection
related to d432b03d62
2020-07-11 11:30:52 -07:00
d432b03d62 fix many module imports causing GC to pull the rug on our module instance 2020-07-10 20:00:17 -07:00
433fbc4019 core; num; add exp & log2
I've had a couple use cases in time that the code is significantly clearer with these, and makes porting less error prone
2020-07-10 19:38:45 -07:00
0e8d56f874 add a note about switched goto for future reference 2020-07-10 19:15:21 -07:00
28ad8aa9e0 compiler; fix incorrect byte length for CODE_IMPORT_VARIABLE
this can lead to some REALLY fun debugging because various code bytes/instructions get skipped, leading to wrong inputs into wrong opcodes and all sorts 💯
2020-07-10 19:14:54 -07:00
b3d496ea36 compiler; rename getNumArguments to be clearer as to the intent 2020-07-10 19:13:44 -07:00
8be40ec14e runFile: Free file source before exiting the function (#774)
This prevents a memleak, noticeable when running `wren_test` under
`valgrind`. For example, the following command would leak

`./bin/wren_test_d any_example.wren`
2020-07-09 11:54:21 -07:00
1623654465 docs; fix embedding tutorial missing a write function so nothing shows up when learning. 2020-06-30 07:40:12 -07:00
e539279121 docs; fix // comments properly this time
...
2020-06-17 09:48:12 -07:00
7651459dfb add // comments to code highlighting, but not with errors in the code 2020-06-17 09:36:34 -07:00
d02903b7d0 add // comments to code highlighting 2020-06-17 09:20:15 -07:00
88043a7cb9 Change wren to wren_cli (#765) 2020-06-17 07:57:03 -07:00
b59c060ccd Small tweaks to error handling. (#762)
* wren/primitive: Remove duplicated declaration introduced in 9f64c05fa.
* wren/primitive: Allow RETURN_ERROR_FMT to have any number of arguments.
* wren/vm: Remove extra validateApiSlot in wrenGetVariable.
(The slot validation is guaranted by setSlot later in the function.)
* wren/primitive: Use RETURN_ERROR_FMT in validateFn.
2020-06-15 11:31:29 -07:00
30b2ebd3f7 fix util/generate_project.py
- fix premake args being incorrect
- remove platform assumptions, making it portable
- start with a best guess based on project layout
- use fallback if not specified or not found
- display errors/help if not found
2020-06-14 19:44:58 -07:00
de6a312868 Functions for operating on Maps from C (#725)
new API functions for maps:
wrenSetSlotNewMap
wrenGetMapCount
wrenGetMapContainsKey
wrenGetMapValue
wrenSetMapValue
wrenRemoveMapValue
2020-06-14 14:45:23 -07:00
344d3432b3 Fix slot array corrupted by wrenInterpret() (#730) 2020-06-13 21:42:06 -07:00
7983082b71 Support positive sign in scientific notation (#706)
* Support positive sign in scientific notation
* Add exponent with positive sign to docs
2020-06-13 21:37:30 -07:00
f3493d0499 Optimize Random.sample(_, _) for performance (#716)
* Optimize Random.sample(_, _) for performance
* Make tests treat random samples as unordered
* Test all sample sizes possible
* Tweak random sampling algorithm for performance
2020-06-13 21:31:23 -07:00
fea0dfafa0 Fix typos in wren_value.h (#749)
* Fix typo: "pointing too" -> "pointing to"
* Fix typo: It *is* heap-allocated
2020-06-13 21:25:55 -07:00
f894273f50 Refactor travis script (#754) 2020-06-13 21:25:18 -07:00
909d1c9471 Add script to regenerate projects using premake (#755) 2020-06-13 21:24:55 -07:00
b5894c6ff5 wren: Remove a magic number in the compiler. (#757) 2020-06-13 21:24:37 -07:00
7c357e1b02 Update gitignore (#759)
Add VSCode project folder and macOS specific stuff.
2020-06-13 21:24:16 -07:00
9fb6d02b5c main branch reference fixes 2020-06-12 10:11:49 -07:00
e45a9d0382 removed localhost (#758) 2020-06-09 17:03:36 -07:00
bef4099101 missed nitpick 2020-06-08 12:31:03 -07:00
9f64c05fa8 Make do blocks in macros consistent with the rest of the code 2020-06-08 12:28:15 -07:00
26d0194117 wren/vm: Uniformize macros to stick to 80 columns. (#756)
uniform macros
2020-06-08 12:23:15 -07:00
2c2f5936eb tests; warn against missing wren_test binary correctly 2020-06-06 10:38:43 -07:00
b694b2231c docs; fix blog template missing links and wrong paths 2020-06-06 10:32:44 -07:00
6cfe6dd6de tests; warn against missing wren_test binary instead of a loud error 2020-06-06 10:29:35 -07:00
8341f61cdb docs; nitpicks 2020-06-05 20:11:32 -07:00
77 changed files with 935 additions and 403 deletions

6
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -13,7 +13,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 +33,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.

View File

@ -47,7 +47,7 @@ involved][contribute]!
[![Build Status](https://travis-ci.org/wren-lang/wren.svg)](https://travis-ci.org/wren-lang/wren)
[syntax]: http://wren.io/syntax.html
[src]: https://github.com/wren-lang/wren/tree/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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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>
&mdash; Made with &#x2764; 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>

View File

@ -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.

View File

@ -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

View File

@ -80,7 +80,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -72,7 +72,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -70,7 +70,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -85,7 +85,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -70,7 +70,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -79,7 +79,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -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

View File

@ -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/

View File

@ -47,22 +47,53 @@ for a module.
The signature of this function is:
<pre class="snippet" data-lang="c">
char* loadModule(WrenVM* vm, const char* name)
WrenLoadModuleResult loadModule(WrenVM* vm, const char* name)
</pre>
When a module is imported, Wren calls this and passes in the module's name. The
host should return the source code for that module. Memory for the source should
be allocated using the same allocator that the VM uses for other allocation (see
below). Wren will take ownership of the returned string and free it later.
host should return the source code for that module in a `WrenLoadModuleResult` struct.
<pre class="snippet" data-lang="c">
WrenLoadModuleResult myLoadModule(WrenVM* vm, const char* name) {
WrenLoadModuleResult result = {0};
result.source = getSourceForModule(name);
return result;
}
</pre>
The module loader is only be called once for any given module name. Wren caches
the result internally so subsequent imports of the same module use the
previously loaded code.
If your host application isn't able to load a module with some name, it should
return `NULL` and Wren will report that as a runtime error.
make sure the `source` value is `NULL` when returned. Wren will then report that as a runtime error.
If you don't use any `import` statements, you can leave this `NULL`.
If you don't use any `import` statements, you can leave the `loadModuleFn` field in
the configuration set to `NULL` (the default).
Additionally, the `WrenLoadModuleResult` allows us to add a callback for when Wren is
done with the `source`, so we can free the memory if needed.
<pre class="snippet" data-lang="c">
static void loadModuleComplete(WrenVM* vm,
const char* module,
WrenLoadModuleResult result)
{
if(result.source) {
//for example, if we used malloc to allocate
our source string, we use free to release it.
free((void*)result.source);
}
}
WrenLoadModuleResult myLoadModule(WrenVM* vm, const char* name) {
WrenLoadModuleResult result = {0};
result.onComplete = loadModuleComplete;
result.source = getSourceForModule(name);
return result;
}
</pre>
### **`bindForeignMethodFn`**

View File

@ -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;
config.writeFn = &writeFn;
wrenInitConfiguration(&config);
</pre>
[configuration]: configuring-the-vm.html

View File

@ -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

View File

@ -80,7 +80,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -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

View File

@ -81,17 +81,17 @@ WrenVM* vm = wrenNewVM(&config);
That function has this signature:
<pre class="snippet" data-lang="c">
char* WrenLoadModuleFn(WrenVM* vm, const char* name);
WrenLoadModuleResult WrenLoadModuleFn(WrenVM* vm, const char* name);
</pre>
Whenever a module is imported, the VM calls this and passes it the name of the
module. The embedder is expected to return the source code contents of the
module. When you embed Wren in your app, you can handle this however you want:
reach out to the file system, look inside resources bundled into your app,
whatever.
module in a `WrenLoadModuleResult`. When you embed Wren in your app, you can handle
this however you want: reach out to the file system, look inside resources bundled
into your app, whatever.
You can return `NULL` from this function to indicate that a module couldn't be
found. When you do this, Wren will report it as a runtime error.
You can return the source field as `NULL` from this function to indicate that a module
couldn't be found. When you do this, Wren will report it as a runtime error.
### The command-line loader

View File

@ -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` (Eulers number) raised to the number. This: `eⁿ`.
### **pow**(power)

View File

@ -92,7 +92,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -69,7 +69,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -69,7 +69,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -98,7 +98,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -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

View File

@ -120,7 +120,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -121,7 +121,7 @@
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; 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>

View File

@ -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.

View File

@ -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 "$<"

View File

@ -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 "$<"

View File

@ -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 "$<"

Binary file not shown.

View File

@ -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" />

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -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 */,

View File

@ -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.
@ -65,8 +65,26 @@ typedef void (*WrenFinalizerFn)(void* data);
typedef const char* (*WrenResolveModuleFn)(WrenVM* vm,
const char* importer, const char* name);
// Forward declare
typedef struct WrenLoadModuleResult WrenLoadModuleResult;
// Called after loadModuleFn is called for module [name]. The original returned result
// is handed back to you in this callback, so that you can free memory if appropriate.
typedef void (*WrenLoadModuleCompleteFn)(WrenVM* vm, const char* name, WrenLoadModuleResult result);
// The result of a loadModuleFn call.
// [length] is optional, a value of 0 means length is ignored.
// [source] is the source code for the module, or NULL if the module is not found.
// [onComplete] an optional callback that will be called once Wren is done with the result.
typedef struct WrenLoadModuleResult
{
const char* source;
WrenLoadModuleCompleteFn onComplete;
void* userData;
} WrenLoadModuleResult;
// Loads and returns the source code for the module [name].
typedef char* (*WrenLoadModuleFn)(WrenVM* vm, const char* name);
typedef WrenLoadModuleResult (*WrenLoadModuleFn)(WrenVM* vm, const char* name);
// Returns a pointer to a foreign method on [className] in [module] with
// [signature].
@ -263,6 +281,7 @@ typedef enum
WREN_TYPE_NUM,
WREN_TYPE_FOREIGN,
WREN_TYPE_LIST,
WREN_TYPE_MAP,
WREN_TYPE_NULL,
WREN_TYPE_STRING,
@ -440,6 +459,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 +492,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,

View File

@ -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) {

View File

@ -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"

View File

@ -33,7 +33,7 @@
// 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
@ -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 )

View File

@ -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);
}
}

View File

@ -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)
{
@ -1299,6 +1301,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);

View File

@ -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)
{

View File

@ -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)

View File

@ -5,23 +5,29 @@
// 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)
// 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 +36,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.

View File

@ -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);

View File

@ -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.

View File

@ -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
@ -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++)
@ -693,44 +701,39 @@ static Value importModule(WrenVM* vm, Value name)
wrenPushRoot(vm, AS_OBJ(name));
WrenLoadModuleResult result = {0};
const char* source = NULL;
bool allocatedSource = true;
// Let the host try to provide the module.
if (vm->config.loadModuleFn != NULL)
{
source = vm->config.loadModuleFn(vm, AS_CSTRING(name));
result = vm->config.loadModuleFn(vm, AS_CSTRING(name));
}
// If the host didn't provide it, see if it's a built in optional module.
if (source == NULL)
if (result.source == NULL)
{
result.onComplete = NULL;
ObjString* nameString = AS_STRING(name);
#if WREN_OPT_META
if (strcmp(nameString->value, "meta") == 0) source = wrenMetaSource();
if (strcmp(nameString->value, "meta") == 0) result.source = wrenMetaSource();
#endif
#if WREN_OPT_RANDOM
if (strcmp(nameString->value, "random") == 0) source = wrenRandomSource();
if (strcmp(nameString->value, "random") == 0) result.source = wrenRandomSource();
#endif
// TODO: Should we give the host the ability to provide strings that don't
// need to be freed?
allocatedSource = false;
}
if (source == NULL)
if (result.source == NULL)
{
vm->fiber->error = wrenStringFormat(vm, "Could not load module '@'.", name);
wrenPopRoot(vm); // name.
return NULL_VAL;
}
ObjClosure* moduleClosure = compileInModule(vm, name, source, false, true);
ObjClosure* moduleClosure = compileInModule(vm, name, result.source, false, true);
// Modules loaded by the host are expected to be dynamically allocated with
// ownership given to the VM, which will free it. The built in optional
// modules are constant strings which don't need to be freed.
if (allocatedSource) DEALLOCATE(vm, (char*)source);
// Now that we're done, give the result back in case there's cleanup to do.
if(result.onComplete) result.onComplete(vm, AS_CSTRING(name), result);
if (moduleClosure == NULL)
{
@ -797,36 +800,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 +846,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
@ -1446,8 +1449,12 @@ WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
wrenPushRoot(vm, (Obj*)closure);
ObjFiber* fiber = wrenNewFiber(vm, closure);
wrenPopRoot(vm); // closure.
return runInterpreter(vm, fiber);
WrenInterpretResult result = runInterpreter(vm, fiber);
vm->fiber = NULL;
vm->apiStack = NULL;
return result;
}
ObjClosure* wrenCompileSource(WrenVM* vm, const char* module, const char* source,
@ -1592,6 +1599,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 +1698,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 +1757,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));

View File

@ -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
{

View File

@ -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;

View File

@ -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"

130
test/api/maps.c Normal file
View 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
View 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
View 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.

View File

@ -13,9 +13,43 @@ 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");
for (int i = 0; i < 5; i++) {
// Calling `wrenEnsureSlots()` before `wrenInterpret()` should not introduce
// problems later.
wrenEnsureSlots(otherVM, 2);
result = wrenInterpret(otherVM, "main", "1 + 2");
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;
}

View File

@ -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

View File

@ -14,7 +14,12 @@ static void reportError(WrenVM* vm, WrenErrorType type,
if (type == WREN_ERROR_RUNTIME) printf("%s\n", message);
}
static char* loadModule(WrenVM* vm, const char* module)
static void loadModuleComplete(WrenVM* vm, const char* module, WrenLoadModuleResult result)
{
free((void*)result.source);
}
static WrenLoadModuleResult loadModule(WrenVM* vm, const char* module)
{
printf("loading %s\n", module);
@ -27,10 +32,14 @@ static char* loadModule(WrenVM* vm, const char* module)
{
source = "System.print(\"ok\")";
}
char* string = (char*)malloc(strlen(source) + 1);
strcpy(string, source);
return string;
WrenLoadModuleResult result = {0};
result.onComplete = loadModuleComplete;
result.source = string;
return result;
}
static void runTestVM(WrenVM* vm, WrenConfiguration* configuration,

View File

@ -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;
}

View File

@ -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

View 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

View 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

View File

@ -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

View File

@ -0,0 +1 @@
var x = 1e+-01 // expect error

View File

@ -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)")
}

View File

@ -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)")
}
}

View File

@ -364,7 +364,15 @@
}
}
char* readModule(WrenVM* vm, const char* module)
void readModuleComplete(WrenVM* vm, const char* module, WrenLoadModuleResult result)
{
if (result.source) {
free((void*)result.source);
result.source = NULL;
}
}
WrenLoadModuleResult readModule(WrenVM* vm, const char* module)
{
Path* filePath = pathNew(module);
@ -375,8 +383,11 @@
char* source = readFile(filePath->chars);
pathFree(filePath);
//may or may not be null
return source;
//source may or may not be null
WrenLoadModuleResult result;
result.source = source;
result.onComplete = readModuleComplete;
return result;
}
@ -441,6 +452,7 @@
WrenInterpretResult result = wrenInterpret(vm, module->chars, source);
pathFree(module);
free(source);
return result;
}

View File

@ -59,7 +59,7 @@ typedef struct
PathType pathType(const char* path);
//file helpers
char* readFile(const char* path);
char* readModule(WrenVM* vm, const char* module);
WrenLoadModuleResult readModule(WrenVM* vm, const char* module);
//vm helpers
void vm_write(WrenVM* vm, const char* text);
void reportError(WrenVM* vm, WrenErrorType type, const char* module, int line, const char* message);

50
util/generate_projects.py Executable file
View 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)

View File

@ -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)