mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-12 14:48:40 +01:00
Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a18fc489f | |||
| d763c72e3d | |||
| 34b01226a4 | |||
| a4ae905384 | |||
| 5244a9d001 | |||
| 9e86b0d26a | |||
| e260b467c4 | |||
| 1307bdfb64 | |||
| 06b71897b0 | |||
| 059e407ed3 | |||
| fd1d095588 | |||
| 7a131a67e6 | |||
| e4052a25d7 | |||
| 041f1bab8d | |||
| 68f5c096d8 | |||
| 4847b37789 | |||
| 28da4b449c | |||
| 61cc6cb745 | |||
| 0ab930c9c2 | |||
| d38c047a5a | |||
| 0be504832e | |||
| 4cd374e1a7 | |||
| 5e60bbf0cf | |||
| 197c0ff4f9 | |||
| e3c76a3e76 | |||
| 8304fd5ecc | |||
| ea684194db | |||
| 981ea4adf1 | |||
| 345f919e26 | |||
| 853f5a3414 | |||
| 11bea3ca01 | |||
| 33ab8be7e3 | |||
| a501fba4bb | |||
| 615a6aa208 | |||
| 94e835cdd6 | |||
| 3c0fe12102 | |||
| ecce1f6be9 | |||
| 4d1d0d972e | |||
| 5b290cacc5 | |||
| dfa7d9c895 | |||
| 2bc895c26d | |||
| 208bbc02a5 | |||
| 9dfbc021a0 | |||
| ae6fdb3da7 | |||
| 3a06580b89 | |||
| 0fa16a20ec | |||
| af5227f03b | |||
| 1720a20979 | |||
| 4ac41c36db | |||
| 79000a320e | |||
| f1225ef7dd | |||
| 57bebd41ca | |||
| 0f8b44e61b | |||
| 96eb68bae3 | |||
| 16ddbb66f8 | |||
| 3ceb029df9 | |||
| ce599259bc | |||
| 97bc340737 | |||
| 81aff84415 | |||
| cb51d61a64 | |||
| 9da1e265df | |||
| 9fbfe6a419 | |||
| 3c07611b0a | |||
| aac6296317 | |||
| a294da7974 | |||
| c572345c3c | |||
| da3a3f0e0e | |||
| 7d3f063e87 | |||
| 4687300ad6 | |||
| 89c5e22480 | |||
| 8361217369 | |||
| 38f50fe091 | |||
| 62009870a8 | |||
| 3d5e68fc01 | |||
| 08d2fa3821 | |||
| d8bdc7359e | |||
| 76fb4f311b | |||
| 84b29e6995 | |||
| a11d66cbd3 | |||
| 59ee326523 | |||
| 182ca90b8c | |||
| bc7dd50a54 | |||
| 97ebcc72c3 | |||
| 999acba06f | |||
| 5264b46246 | |||
| 3e0f71b742 | |||
| 6bd2f810e2 | |||
| e7071fffa5 | |||
| 55b926410d | |||
| f5339993ce | |||
| 473392a56a | |||
| 556eeac86e | |||
| 94e4888b6a | |||
| 44d6d20586 | |||
| ad4e039187 | |||
| 3c475f01ee | |||
| 4c496c56a6 | |||
| 1c5ac28831 | |||
| 45c67fae0c | |||
| beae242a41 | |||
| 86463acb90 | |||
| 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.
|
||||
|
||||
6
AUTHORS
6
AUTHORS
@ -25,4 +25,8 @@ Michal Kozakiewicz <michalkozakiewicz3@gmail.com>
|
||||
Charlotte Koch <cfkoch@edgebsd.org>
|
||||
Michel Hermier <michel.hermier@gmail.com>
|
||||
Taylor Hoff <primdevs@gmail.com>
|
||||
ruby0x1 <ruby0x1@pm.me>
|
||||
ruby0x1 <ruby0x1@pm.me>
|
||||
Kolja Kube <code@koljaku.be>
|
||||
Alexander Klingenbeck <alexander.klingenbeck@gmx.de>
|
||||
Aviv Beeri <avbeeri@gmail.com>
|
||||
|
||||
|
||||
55
CHANGELOG.md
55
CHANGELOG.md
@ -1,3 +1,54 @@
|
||||
## 0.4.0
|
||||
|
||||
### Language
|
||||
- Add `continue` keyword
|
||||
- Add `as`: `import "..." for Name as OtherName`
|
||||
- Add Support positive sign in scientific notation
|
||||
- Add Fiber.try(value) to complement Fiber.call(value)
|
||||
- Allow `.` to be on a different line (for fluent/builder APIs)
|
||||
|
||||
### Modules
|
||||
- Random: Random.sample optimizations
|
||||
- List:
|
||||
- add `list.sort()` and `list.sort {|a, b| ... }` (quicksort)
|
||||
- add `list.swap(index0, index1)` for swapping elements within a list
|
||||
- add `list.indexOf(value)` for finding values in a list
|
||||
- Num:
|
||||
- add `Num.tau`
|
||||
- add `Num.nan`
|
||||
- add `Num.infinity`
|
||||
- add `min(other)`
|
||||
- add `max(other)`
|
||||
- add `clamp(min, max)`
|
||||
- add `exp`
|
||||
- add `log2`
|
||||
|
||||
### Fixes
|
||||
- Fix stack corruption related to `Fn` calls
|
||||
- Fix a byte offset bug in CODE_IMPORT_VARIABLE
|
||||
- Fix some stack corruptions related to multiple wrenInterpret calls
|
||||
- Fixed crash when GC collects module during import
|
||||
- Fix `Bool`, `Num` and `Null` allowing subclassing, which is invalid
|
||||
|
||||
### API
|
||||
- BREAKING: Add `userData` to `wrenReallocateFn`
|
||||
- BREAKING: Add `WrenLoadModuleResult` which has a `onComplete` callback, allowing freeing module strings
|
||||
- Add `wrenHasVariable` and `wrenHasModule` queries, for use with `wrenGetVariable`
|
||||
- Add `wrenSetListElement` to complement `wrenGetListElement`, and allow negative index for both
|
||||
- Add Map functions to API
|
||||
- wrenSetSlotNewMap
|
||||
- wrenGetMapCount
|
||||
- wrenGetMapContainsKey
|
||||
- wrenGetMapValue
|
||||
- wrenSetMapValue
|
||||
- wrenRemoveMapValue
|
||||
|
||||
### Other
|
||||
- build; add util/generate_docs.py for regenerating project files
|
||||
- vm; Allow computed goto when using clang on Windows
|
||||
- vm; WREN_MAX_TEMP_ROOTS default is 8 (instead of 5)
|
||||
- vm; GC debug times are printed in milliseconds, not seconds
|
||||
|
||||
## 0.3.0
|
||||
|
||||
0.3.0 is a fairly specific release, aimed at fixing build issues across platforms,
|
||||
@ -13,7 +64,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 +84,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.
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-2020 Robert Nystrom and Wren Contributors
|
||||
Copyright (c) 2013-2021 Robert Nystrom and Wren Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@ -44,10 +44,10 @@ If you like the sound of this, [let's get started][started]. You can even try
|
||||
it [in your browser][browser]! Excited? Well, come on and [get
|
||||
involved][contribute]!
|
||||
|
||||
[](https://travis-ci.org/wren-lang/wren)
|
||||
[](https://travis-ci.org/wren-lang/wren)
|
||||
|
||||
[syntax]: http://wren.io/syntax.html
|
||||
[src]: https://github.com/wren-lang/wren/tree/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>
|
||||
|
||||
@ -267,7 +267,7 @@ inside a method works like this:
|
||||
|
||||
So, in the above example, we hit case #2 and it prints "Francis". Distinguishing
|
||||
self sends from outer variables based on the *case* of the first letter in the
|
||||
name probably seems crazy but it works surprisingly well. Method names are
|
||||
name probably seems weird but it works surprisingly well. Method names are
|
||||
lowercase in Wren. Class names are capitalized.
|
||||
|
||||
Most of the time, when you're in a method and want to access a name from outside
|
||||
@ -347,6 +347,20 @@ overloaded by [arity](#signature). A constructor *must* be a named method with
|
||||
a (possibly empty) argument list. Operators, getters, and setters cannot be
|
||||
constructors.
|
||||
|
||||
A constructor returns the instance of the class being created, even if you
|
||||
don't explicitly use `return`. It is valid to use `return` inside of a
|
||||
constructor, but it is an error to have an expression after the return.
|
||||
That rule applies to `return this` as well, return handles that implicitly inside
|
||||
a constructor, so just `return` is enough.
|
||||
|
||||
<pre class="snippet">
|
||||
return //> valid, returns 'this'
|
||||
|
||||
return variable //> invalid
|
||||
return null //> invalid
|
||||
return this //> also invalid
|
||||
</pre>
|
||||
|
||||
A constructor is actually a pair of methods. You get a method on the class:
|
||||
|
||||
<pre class="snippet">
|
||||
@ -621,6 +635,131 @@ class Derived is Base {
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
## Attributes
|
||||
|
||||
<small>**experimental stage**: subject to minor changes</small>
|
||||
|
||||
A class and methods within a class can be tagged with 'meta attributes'.
|
||||
|
||||
Like this:
|
||||
|
||||
<pre class="snippet">
|
||||
#hidden = true
|
||||
class Example {}
|
||||
</pre>
|
||||
|
||||
These attributes are metadata, they give you a way to annotate and store
|
||||
any additional information about a class, which you can optionally access at runtime.
|
||||
This information can also be used by external tools, to provide additional
|
||||
hints and information from code to the tool.
|
||||
|
||||
<small>
|
||||
Since this feature has just been introduced, **take note**.
|
||||
|
||||
**Currently** there are no attributes with a built-in meaning.
|
||||
Attributes are user-defined metadata. This may not remain
|
||||
true as some may become well defined through convention or potentially
|
||||
through use by Wren itself.
|
||||
</small>
|
||||
|
||||
Attributes are placed before a class or method definition,
|
||||
and use the `#` hash/pound symbol.
|
||||
|
||||
They can be
|
||||
|
||||
- a `#key` on it's own
|
||||
- a `#key = value`
|
||||
- a `#group(with, multiple = true, keys = "value")`
|
||||
|
||||
An attribute _key_ can only be a `Name`. This is the same type of name
|
||||
as a method name, a class name or variable name, an identifier that matches
|
||||
the Wren identifier rules. A name results in a String value at runtime.
|
||||
|
||||
An attribute _value_ can be any of these literal values: `Name, String, Bool, Num`.
|
||||
Values cannot contain expressions, just a value, there is no compile time
|
||||
evaluation.
|
||||
|
||||
Groups can span multiple lines, methods have their own attributes, and duplicate
|
||||
keys are valid.
|
||||
|
||||
<pre class="snippet">
|
||||
#key
|
||||
#key = value
|
||||
#group(
|
||||
multiple,
|
||||
lines = true,
|
||||
lines = 0
|
||||
)
|
||||
class Example {
|
||||
#test(skip = true, iterations = 32)
|
||||
doStuff() {}
|
||||
}
|
||||
</pre>
|
||||
|
||||
### Accessing attributes at runtime
|
||||
|
||||
By default, attributes are compiled out and ignored.
|
||||
|
||||
For an attribute to be visible at runtime, mark it for runtime
|
||||
access using an exclamation:
|
||||
|
||||
<pre class="snippet">
|
||||
#doc = "not runtime data"
|
||||
#!runtimeAccess = true
|
||||
#!maxIterations = 16
|
||||
</pre>
|
||||
|
||||
Attributes at runtime are stored on the class. You can access them via
|
||||
`YourClass.attributes`. The `attributes` field on a class will
|
||||
be null if a class has no attributes or if it's attributes aren't marked.
|
||||
|
||||
If the class contains class or method attributes, it will be an object with
|
||||
two getters:
|
||||
|
||||
- `YourClass.attributes.self` for the class attributes
|
||||
- `YourClass.attributes.methods` for the method attributes
|
||||
|
||||
Attributes are stored by group in a regular Wren Map.
|
||||
Keys that are not grouped, use `null` as the group key.
|
||||
|
||||
Values are stored in a list, since duplicate keys are allowed, multiple
|
||||
values need to be stored. They're stored in order of definition.
|
||||
|
||||
Method attributes are stored in a map by method signature, and each method
|
||||
has it's own attributes that match the above structure. The method signature
|
||||
is prefixed by `static` or `foreign static` as needed.
|
||||
|
||||
Let's see what that looks like:
|
||||
|
||||
<pre class="snippet">
|
||||
// Example.attributes.self =
|
||||
// {
|
||||
// null: { "key":[null] },
|
||||
// group: { "key":[value, 32, false] }
|
||||
// }
|
||||
|
||||
#!key
|
||||
#ignored //compiled out
|
||||
#!group(key=value, key=32, key=false)
|
||||
class Example {
|
||||
#!getter
|
||||
getter {}
|
||||
|
||||
// { regular(_,_): { regular:[null] } }
|
||||
#!regular
|
||||
regular(arg0, arg1) {}
|
||||
|
||||
// { static other(): { isStatic:[true] } }
|
||||
#!isStatic = true
|
||||
static other()
|
||||
|
||||
// { foreign static example(): { isForeignStatic:[32] } }
|
||||
#!isForeignStatic=32
|
||||
foreign static example()
|
||||
}
|
||||
</pre>
|
||||
|
||||
<br><hr>
|
||||
<a class="right" href="concurrency.html">Concurrency →</a>
|
||||
<a href="functions.html">← Functions</a>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
^title Module "scheduler"
|
||||
|
||||
**TODO**
|
||||
This module provides a vehicle to allow other operations to be performed asynchronously whilst waiting for the main operation to be completed.
|
||||
|
||||
It contains a single class:
|
||||
|
||||
* [Scheduler](scheduler.html)
|
||||
|
||||
@ -1,7 +1,27 @@
|
||||
^title Scheduler Class
|
||||
|
||||
**TODO**
|
||||
The Scheduler class maintains a list of fibers, to be started one after the other, when a signal to do so is received. The signal (a private method call) is typically transmitted by _long running_ methods in the File or Timer classes which suspend the current fiber so that Wren can carry out other tasks in the meantime.
|
||||
|
||||
## Methods
|
||||
## Static Method
|
||||
|
||||
**TODO**
|
||||
### Scheduler.**add**(callable)
|
||||
|
||||
Adds a new fiber to the scheduler's fibers list. This fiber calls `callable` and then transfers to the next fiber in the list, if there is one.
|
||||
|
||||
`callable` is a function or other object which has a call() method.
|
||||
|
||||
<pre class="snippet">
|
||||
var a = 3
|
||||
|
||||
Scheduler.add {
|
||||
a = a * a
|
||||
}
|
||||
|
||||
Scheduler.add {
|
||||
a = a + 1
|
||||
}
|
||||
|
||||
System.print(a) // still 3
|
||||
Timer.sleep(3000) // wait 3 seconds
|
||||
System.print(a) // now 3 * 3 + 1 = 10
|
||||
</pre>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
^title Module "timer"
|
||||
|
||||
**TODO**
|
||||
This module provides a mechanism to suspend the current fiber for a given period of time either as a simple delay or to allow other operations to be performed asynchronously in the meantime.
|
||||
|
||||
It contains a single class:
|
||||
|
||||
* [Timer](timer.html)
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
^title Timer Class
|
||||
|
||||
**TODO**
|
||||
## Static Method
|
||||
|
||||
## Methods
|
||||
### Timer.**sleep**(milliseconds)
|
||||
|
||||
Suspends the current fiber for the given number of milliseconds. It is a runtime error if this is not a non-negative number.
|
||||
|
||||
This method is often used in conjunction with the Scheduler class which runs any scheduled tasks in separate fibers whilst the current fiber is sleeping.
|
||||
|
||||
Note that this method also suspends the System.clock method which will not give the correct running time for a program as a result.
|
||||
|
||||
**TODO**
|
||||
|
||||
@ -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
|
||||
|
||||
@ -88,8 +88,7 @@ The basic process is simple:
|
||||
If there are no failures, you're good to go.
|
||||
|
||||
2. **[Fork the repo][fork] so you can change it locally.** Please make your
|
||||
changes in separate [feature branches][] to make things a little easier on
|
||||
me.
|
||||
changes in separate [feature branches][] to make things a little easier.
|
||||
|
||||
3. **Change the code.** Please follow the style of the surrounding code. That
|
||||
basically means `camelCase` names, `{` on the next line, keep within 80
|
||||
@ -105,8 +104,7 @@ The basic process is simple:
|
||||
6. **Add your name and email to the [AUTHORS][] file if you haven't already.**
|
||||
|
||||
7. **Send a [pull request][].** Pat yourself on the back for contributing to a
|
||||
fun open source project! I'll take it from here and hopefully we'll get it
|
||||
landed smoothly.
|
||||
fun open source project!
|
||||
|
||||
## Getting help
|
||||
|
||||
@ -118,11 +116,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/
|
||||
|
||||
@ -65,7 +65,7 @@ if (ready) {
|
||||
|
||||
Unlike most other [operators][] in Wren which are just a special syntax for
|
||||
[method calls][], the `&&` and `||` operators are special. This is because they
|
||||
only conditionally evaluate right operand—they short-circuit.
|
||||
only conditionally evaluate the right operand—they short-circuit.
|
||||
|
||||
[operators]: method-calls.html#operators
|
||||
[method calls]: method-calls.html
|
||||
@ -92,7 +92,7 @@ System.print(1 || 2) //> 1
|
||||
|
||||
Also known as the "ternary" operator since it takes three arguments, Wren has
|
||||
the little "if statement in the form of an expression" you know and love from C
|
||||
and its brethren.
|
||||
and similar languages.
|
||||
|
||||
<pre class="snippet">
|
||||
System.print(1 != 2 ? "math is sane" : "math is not sane!")
|
||||
@ -177,6 +177,21 @@ for (i in [1, 2, 3, 4]) {
|
||||
} //> 3
|
||||
</pre>
|
||||
|
||||
## Continue statements
|
||||
|
||||
During the execution of a loop body, you might decide that you want to skip the
|
||||
rest of this iteration and move on to the next one. You can use a `continue`
|
||||
statement to do that. It's just the `continue` keyword all by itself. Execution
|
||||
will immediately jump to the beginning of the next loop iteration (and check the
|
||||
loop conditions).
|
||||
|
||||
<pre class="snippet">
|
||||
for (i in [1, 2, 3, 4]) {
|
||||
System.print(i) //> 1
|
||||
if (i == 2) continue //> 3
|
||||
} //> 4
|
||||
</pre>
|
||||
|
||||
## Numeric ranges
|
||||
|
||||
Lists are one common use for `for` loops, but sometimes you want to walk over a
|
||||
|
||||
@ -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`**
|
||||
|
||||
@ -151,7 +182,7 @@ These fields control how the VM allocates and manages memory.
|
||||
This lets you provide a custom memory allocation function. Its signature is:
|
||||
|
||||
<pre class="snippet" data-lang="c">
|
||||
void* reallocate(void* memory, size_t newSize)
|
||||
void* reallocate(void* memory, size_t newSize, void* userData)
|
||||
</pre>
|
||||
|
||||
Wren uses this one function to allocate, grow, shrink, and deallocate memory.
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -1,129 +1,43 @@
|
||||
^title Functions
|
||||
|
||||
No self-respecting language today can get by without functions—first
|
||||
class little bundles of code. Since Wren is object-oriented, most of your code
|
||||
will live in methods on classes, but free-floating functions are still
|
||||
eminently handy.
|
||||
Like many languages today, functions in Wren are little bundles of code
|
||||
you can store in a variable, or pass as an argument to a method.
|
||||
|
||||
Notice there's a difference between _function_ and _method_.
|
||||
|
||||
Since Wren is object-oriented, most of your code will live in methods on
|
||||
classes, but free-floating functions are still eminently handy.
|
||||
|
||||
Functions are objects like everything else in Wren, instances of the `Fn`
|
||||
class.
|
||||
|
||||
## Block arguments
|
||||
## Creating a function
|
||||
|
||||
Most of the time you create a function just to pass it to some method. For
|
||||
example, if you want to filter a [list](lists.html) by some criteria, you'll
|
||||
call its `where` method, passing in a function that defines the predicate
|
||||
you're filtering on.
|
||||
|
||||
Since that's the most common usage pattern, Wren's syntax optimizes for that.
|
||||
Taking a page from Ruby, a function is created by passing a *block argument* to
|
||||
a method. At its simplest, it looks like this:
|
||||
To create a function, we call `Fn.new`, which takes a block to execute.
|
||||
To call the function, we use `.call()` on the function instance.
|
||||
|
||||
<pre class="snippet">
|
||||
blondie.callMe {
|
||||
System.print("This is the body!")
|
||||
}
|
||||
var sayHello = Fn.new { System.print("hello") }
|
||||
|
||||
sayHello.call() //> hello
|
||||
</pre>
|
||||
|
||||
Here we're invoking the `callMe` method on `blondie`. We're passing one
|
||||
argument, a function whose body is the
|
||||
following [block](syntax.html#blocks)—everything between that pair of
|
||||
curly braces.
|
||||
|
||||
Methods that take a block argument receive it as a normal parameter. `callMe`
|
||||
could be defined like so:
|
||||
|
||||
<pre class="snippet">
|
||||
class Blondie {
|
||||
callMe(fn) {
|
||||
// Call it...
|
||||
}
|
||||
}
|
||||
|
||||
var blondie = Blondie.new()
|
||||
</pre>
|
||||
|
||||
A method can take other arguments in addition to the block. They appear before
|
||||
the block just like a regular argument list. For example:
|
||||
|
||||
<pre class="snippet">
|
||||
blondie.callMeAt(867, 5309) {
|
||||
System.print("This is the body!")
|
||||
}
|
||||
</pre>
|
||||
|
||||
Of course, you don't *have* to use a block argument to pass a function to a
|
||||
method. If you already have a function object, you can pass it like a regular
|
||||
argument:
|
||||
|
||||
<pre class="snippet">
|
||||
var someFn = // Get a function...
|
||||
blondie.callMe(someFn)
|
||||
</pre>
|
||||
|
||||
Block arguments are purely sugar for creating a function and passing it in one
|
||||
little blob of syntax. There are some times when you want to create a function
|
||||
but *don't* need to pass it to a method. For that, you can call the `Fn`
|
||||
class's constructor:
|
||||
|
||||
<pre class="snippet">
|
||||
var someFn = Fn.new {
|
||||
System.print("Hi!")
|
||||
}
|
||||
</pre>
|
||||
|
||||
As you can see it takes a block argument too! All the constructor does it
|
||||
return that, so this exists purely as a convenience method for you.
|
||||
|
||||
## Calling functions
|
||||
|
||||
Once you have a function, how do you invoke it? Like everything in Wren, you do
|
||||
so by calling a method on it:
|
||||
|
||||
<pre class="snippet">
|
||||
class Blondie {
|
||||
callMe(fn) {
|
||||
fn.call()
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
Functions expose a `call()` method that executes the body of the function. This
|
||||
method is dynamically-dispatched like any other, so you can define your own
|
||||
"function-like" classes and pass them to methods that expect "real" functions.
|
||||
|
||||
<pre class="snippet">
|
||||
class FakeFn {
|
||||
call() {
|
||||
System.print("I'm feeling functional!")
|
||||
}
|
||||
}
|
||||
|
||||
blondie.callMe(FakeFn.new())
|
||||
</pre>
|
||||
Note that we'll see a shorthand syntax for creating a function below.
|
||||
|
||||
## Function parameters
|
||||
|
||||
Of course, functions aren't very useful if you can't pass values to them. The
|
||||
functions that we've seen so far take no arguments. To change that, you can
|
||||
provide a parameter list surrounded by `|` immediately after the opening brace
|
||||
of the body, like so:
|
||||
function above takes no arguments. To change that, you can provide a parameter
|
||||
list surrounded by `|` immediately after the opening brace of the body.
|
||||
|
||||
To pass arguments to the function, pass them to the `call` method:
|
||||
|
||||
<pre class="snippet">
|
||||
blondie.callMe {|first, last|
|
||||
System.print("Hi, " + first + " " + last + "!")
|
||||
var sayMessage = Fn.new {|recipient, message|
|
||||
System.print("message for %(recipient): %(message)")
|
||||
}
|
||||
</pre>
|
||||
|
||||
Here we're passing a function to `callMe` that takes two parameters, `first` and
|
||||
`last`. They are passed to the function when it's called:
|
||||
|
||||
<pre class="snippet">
|
||||
class Blondie {
|
||||
callMe(fn) {
|
||||
fn.call("Debbie", "Harry")
|
||||
}
|
||||
}
|
||||
sayMessage.call("Bob", "Good day!")
|
||||
</pre>
|
||||
|
||||
It's an error to call a function with fewer arguments than its parameter list
|
||||
@ -148,6 +62,14 @@ Fn.new {
|
||||
}
|
||||
</pre>
|
||||
|
||||
The return value is handed back to you when using `call`:
|
||||
|
||||
<pre class="snippet">
|
||||
var fn = Fn.new { "some value" }
|
||||
var result = fn.call()
|
||||
System.print(result) //> some value
|
||||
</pre>
|
||||
|
||||
## Closures
|
||||
|
||||
As you expect, functions are closures—they can access variables defined
|
||||
@ -175,6 +97,146 @@ System.print(counter.call()) //> 2
|
||||
System.print(counter.call()) //> 3
|
||||
</pre>
|
||||
|
||||
## Callable classes
|
||||
|
||||
Because `Fn` is a class, and responds to `call()`, any class can respond to
|
||||
`call()` and be used in place of a function. This is particularly handy when
|
||||
the function is passed to a method to be called, like a callback or event.
|
||||
|
||||
<pre class="snippet">
|
||||
class Callable {
|
||||
construct new() {}
|
||||
call(name, version) {
|
||||
System.print("called %(name) with version %(version)")
|
||||
}
|
||||
}
|
||||
|
||||
var fn = Callable.new()
|
||||
fn.call("wren", "0.4.0")
|
||||
</pre>
|
||||
|
||||
## Block arguments
|
||||
|
||||
Very frequently, functions are passed to methods to be called. There are
|
||||
countless examples of this in Wren, like [list](lists.html) can be filtered
|
||||
using a method `where` which accepts a function:
|
||||
|
||||
<pre class="snippet">
|
||||
var list = [1, 2, 3, 4, 5]
|
||||
var filtered = list.where(Fn.new {|value| value > 3 })
|
||||
System.print(filtered.toList) //> [4, 5]
|
||||
</pre>
|
||||
|
||||
This syntax is a bit less fun to read and write, so Wren implements the
|
||||
_block argument_ concept. When a function is being passed to a method,
|
||||
and is the last argument to the method, it can use a shorter syntax:
|
||||
_just the block part_.
|
||||
|
||||
Let's use a block argument for `list.where`, it's the last (only) argument:
|
||||
|
||||
<pre class="snippet">
|
||||
var list = [1, 2, 3, 4, 5]
|
||||
var filtered = list.where {|value| value > 3 }
|
||||
System.print(filtered.toList) //> [4, 5]
|
||||
</pre>
|
||||
|
||||
We've seen this before in a previous page using `map` and `where`:
|
||||
|
||||
<pre class="snippet">
|
||||
numbers.map {|n| n * 2 }.where {|n| n < 100 }
|
||||
</pre>
|
||||
|
||||
## Block argument example
|
||||
|
||||
Let's look at a complete example, so we can see both ends.
|
||||
|
||||
Here's a fictional class for something that will call a function
|
||||
when a click event is sent to it. It allows us to pass just a
|
||||
function and assume the left mouse button, or to pass a button and a function.
|
||||
|
||||
<pre class="snippet">
|
||||
class Clickable {
|
||||
construct new() {
|
||||
_fn = null
|
||||
_button = 0
|
||||
}
|
||||
|
||||
onClick(fn) {
|
||||
_fn = fn
|
||||
}
|
||||
|
||||
onClick(button, fn) {
|
||||
_button = button
|
||||
_fn = fn
|
||||
}
|
||||
|
||||
fireEvent(button) {
|
||||
if(_fn && button == _button) {
|
||||
_fn.call(button)
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
Now that we've got the clickable class, let's use it.
|
||||
We'll start by using the method that accepts just a function
|
||||
because we're fine with it just being the default left mouse button.
|
||||
|
||||
<pre class="snippet">
|
||||
var link = Clickable.new()
|
||||
|
||||
link.onClick {|button|
|
||||
System.print("I was clicked by button %(button)")
|
||||
}
|
||||
|
||||
// send a left mouse click
|
||||
// normally this would happen from elsewhere
|
||||
|
||||
link.fireEvent(0) //> I was clicked by button 0
|
||||
</pre>
|
||||
|
||||
Now let's try with the extra button argument:
|
||||
|
||||
<pre class="snippet">
|
||||
var contextMenu = Clickable.new()
|
||||
|
||||
contextMenu.onClick(1) {|button|
|
||||
System.print("I was right-clicked")
|
||||
}
|
||||
|
||||
link.fireEvent(0) //> (nothing happened)
|
||||
link.fireEvent(1) //> I was right-clicked
|
||||
</pre>
|
||||
|
||||
Notice that we still pass the other arguments normally,
|
||||
it's only the last argument that is special.
|
||||
|
||||
**Just a regular function**
|
||||
|
||||
Block arguments are purely syntax sugar for creating a function and passing it
|
||||
in one little blob of syntax. These two are equivalent:
|
||||
|
||||
<pre class="snippet">
|
||||
onClick(Fn.new { System.print("clicked") })
|
||||
onClick { System.print("clicked") }
|
||||
</pre>
|
||||
|
||||
And this is just as valid:
|
||||
|
||||
<pre class="snippet">
|
||||
var onEvent = Fn.new {|button|
|
||||
System.print("clicked by button %(button)")
|
||||
}
|
||||
|
||||
onClick(onEvent)
|
||||
onClick(1, onEvent)
|
||||
</pre>
|
||||
|
||||
**Fn.new**
|
||||
As you may have noticed by now, `Fn` accepts a block argument for the `Fn.new`.
|
||||
All the constructor does is return that argument right back to you!
|
||||
|
||||
|
||||
<br><hr>
|
||||
<a class="right" href="classes.html">Classes →</a>
|
||||
<a href="variables.html">← Variables</a>
|
||||
|
||||
@ -59,7 +59,9 @@ Since it has no dependencies this is simple, all the code in `src/` comes along.
|
||||
If you want an even simpler way, there's an 'amalgamated' build (often called `blob`, or `unity` builds.).
|
||||
This is _all of the wren source code in one file_.
|
||||
|
||||
This file can be generated by running `python3 util/generate_amalgamation.py`, and the generated output will be in `build/wren.c`.
|
||||
This file can be generated by running `python3 util/generate_amalgamation.py > build/wren.c`,
|
||||
which saves the generated output in `build/wren.c`.
|
||||
|
||||
Include `build/wren.c` and `src/include/wren.h` in your project code and you're good to go.
|
||||
<small>Ideally later we can automate generating this and include it in the repo.</small>
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -20,23 +20,23 @@ element you want. Like most languages, indexes start at zero:
|
||||
[subscript operator]: method-calls.html#subscripts
|
||||
|
||||
<pre class="snippet">
|
||||
var hirsute = ["sideburns", "porkchops", "'stache", "goatee"]
|
||||
System.print(hirsute[0]) //> sideburns
|
||||
System.print(hirsute[1]) //> porkchops
|
||||
var trees = ["cedar", "birch", "oak", "willow"]
|
||||
System.print(trees[0]) //> cedar
|
||||
System.print(trees[1]) //> birch
|
||||
</pre>
|
||||
|
||||
Negative indices counts backwards from the end:
|
||||
|
||||
<pre class="snippet">
|
||||
System.print(hirsute[-1]) //> goatee
|
||||
System.print(hirsute[-2]) //> 'stache
|
||||
System.print(trees[-1]) //> willow
|
||||
System.print(trees[-2]) //> oak
|
||||
</pre>
|
||||
|
||||
It's a runtime error to pass an index outside of the bounds of the list. If you
|
||||
don't know what those bounds are, you can find out using count:
|
||||
|
||||
<pre class="snippet">
|
||||
System.print(hirsute.count) //> 4
|
||||
System.print(trees.count) //> 4
|
||||
</pre>
|
||||
|
||||
## Slices and ranges
|
||||
@ -45,7 +45,7 @@ Sometimes you want to copy a chunk of elements from a list. You can do that by
|
||||
passing a [range](values.html#ranges) to the subscript operator, like so:
|
||||
|
||||
<pre class="snippet">
|
||||
System.print(hirsute[1..2]) //> [porkchops, 'stache]
|
||||
System.print(trees[1..2]) //> [birch, oak]
|
||||
</pre>
|
||||
|
||||
This returns a new list containing the elements of the original list whose
|
||||
@ -56,7 +56,7 @@ Negative bounds also work like they do when passing a single number, so to copy
|
||||
a list, you can just do:
|
||||
|
||||
<pre class="snippet">
|
||||
hirsute[0..-1]
|
||||
trees[0..-1]
|
||||
</pre>
|
||||
|
||||
## Adding elements
|
||||
@ -65,22 +65,22 @@ Lists are *mutable*, meaning their contents can be changed. You can swap out an
|
||||
existing element in the list using the subscript setter:
|
||||
|
||||
<pre class="snippet">
|
||||
hirsute[1] = "muttonchops"
|
||||
System.print(hirsute[1]) //> muttonchops
|
||||
trees[1] = "spruce"
|
||||
System.print(trees[1]) //> spruce
|
||||
</pre>
|
||||
|
||||
It's an error to set an element that's out of bounds. To grow a list, you can
|
||||
use `add` to append a single item to the end:
|
||||
|
||||
<pre class="snippet">
|
||||
hirsute.add("goatee")
|
||||
System.print(hirsute.count) //> 5
|
||||
trees.add("maple")
|
||||
System.print(trees.count) //> 5
|
||||
</pre>
|
||||
|
||||
You can insert a new element at a specific position using `insert`:
|
||||
|
||||
<pre class="snippet">
|
||||
hirsute.insert(2, "soul patch")
|
||||
trees.insert(2, "hickory")
|
||||
</pre>
|
||||
|
||||
The first argument is the index to insert at, and the second is the value to
|
||||
@ -113,26 +113,38 @@ System.print(combined) //> [a, b, c, d, e, f]
|
||||
## Removing elements
|
||||
|
||||
The opposite of `insert` is `removeAt`. It removes a single element from a
|
||||
given position in the list. All following items are shifted up to fill in the
|
||||
gap:
|
||||
given position in the list.
|
||||
|
||||
To remove a specific _value_ instead, use `remove`. The first value that
|
||||
matches using regular equality will be removed.
|
||||
|
||||
In both cases, all following items are shifted up to fill in the gap.
|
||||
|
||||
<pre class="snippet">
|
||||
var letters = ["a", "b", "c", "d"]
|
||||
letters.removeAt(1)
|
||||
System.print(letters) //> [a, c, d]
|
||||
letters.remove("a")
|
||||
System.print(letters) //> [c, d]
|
||||
</pre>
|
||||
|
||||
The `removeAt` method returns the removed item:
|
||||
Both the `remove` and `removeAt` method return the removed item:
|
||||
|
||||
<pre class="snippet">
|
||||
System.print(letters.removeAt(1)) //> c
|
||||
</pre>
|
||||
|
||||
If `remove` couldn't find the value in the list, it returns null:
|
||||
|
||||
<pre class="snippet">
|
||||
System.print(letters.remove("not found")) //> null
|
||||
</pre>
|
||||
|
||||
If you want to remove everything from the list, you can clear it:
|
||||
|
||||
<pre class="snippet">
|
||||
hirsute.clear()
|
||||
System.print(hirsute) //> []
|
||||
trees.clear()
|
||||
System.print(trees) //> []
|
||||
</pre>
|
||||
|
||||
<br><hr>
|
||||
|
||||
@ -9,22 +9,26 @@ curly braces. Each entry is a key and a value separated by a colon:
|
||||
|
||||
<pre class="snippet">
|
||||
{
|
||||
"George": "Harrison",
|
||||
"John": "Lennon",
|
||||
"Paul": "McCartney",
|
||||
"Ringo": "Starr"
|
||||
"maple": "Sugar Maple (Acer Saccharum)",
|
||||
"larch": "Alpine Larch (Larix Lyallii)",
|
||||
"oak": "Red Oak (Quercus Rubra)",
|
||||
"fir": "Fraser Fir (Abies Fraseri)"
|
||||
}
|
||||
</pre>
|
||||
|
||||
This creates a map that associates the first name of each Beatle with his last
|
||||
name. Syntactically, in a map literal, keys can be any literal, a variable
|
||||
name, or a parenthesized expression. Values can be any expression. Here, we're
|
||||
using string literals for both keys and values.
|
||||
This creates a map that associates a type of tree (key) to a specific
|
||||
tree within that family (value). Syntactically, in a map literal, keys
|
||||
can be any literal, a variable name, or a parenthesized expression.
|
||||
Values can be any expression. Here, we're using string literals for both keys
|
||||
and values.
|
||||
|
||||
*Semantically*, values can be any object, and multiple keys may map to the same
|
||||
value. Keys have a few limitations. They must be one of the immutable built-in
|
||||
value.
|
||||
|
||||
Keys have a few limitations. They must be one of the immutable built-in
|
||||
[value types][] in Wren. That means a number, string, range, bool, or `null`.
|
||||
You can also use a [class object][] as a key.
|
||||
You can also use a [class object][] as a key (not an instance of that class,
|
||||
the actual class itself).
|
||||
|
||||
[value types]: values.html
|
||||
[class object]: classes.html
|
||||
@ -67,12 +71,12 @@ doesn't necessarily mean the key wasn't found.
|
||||
To tell definitively if a key exists, you can call `containsKey()`:
|
||||
|
||||
<pre class="snippet">
|
||||
var belief = {"nihilism": null}
|
||||
var capitals = {"Georgia": null}
|
||||
|
||||
System.print(belief["nihilism"]) //> null (though key exists)
|
||||
System.print(belief["solipsism"]) //> null
|
||||
System.print(belief.containsKey("nihilism")) //> true
|
||||
System.print(belief.containsKey("solipsism")) //> false
|
||||
System.print(capitals["Georgia"]) //> null (though key exists)
|
||||
System.print(capitals["Idaho"]) //> null
|
||||
System.print(capitals.containsKey("Georgia")) //> true
|
||||
System.print(capitals.containsKey("Idaho")) //> false
|
||||
</pre>
|
||||
|
||||
You can see how many entries a map contains using `count`:
|
||||
@ -113,16 +117,38 @@ System.print(capitals.count) //> 0
|
||||
|
||||
The subscript operator works well for finding values when you know the key
|
||||
you're looking for, but sometimes you want to see everything that's in the map.
|
||||
For that, map exposes two methods: `keys` and `values`.
|
||||
You can use a regular for loop to iterate the contents, and map exposes two
|
||||
additional methods to access the contents: `keys` and `values`.
|
||||
|
||||
The first returns a [Sequence][] that [iterates][] over all of the keys in the
|
||||
map, and the second returns one that iterates over the values.
|
||||
The `keys` method on a map returns a [Sequence][] that [iterates][] over all of
|
||||
the keys in the map, and the `values` method returns one that iterates over the values.
|
||||
|
||||
[sequence]: modules/core/sequence.html
|
||||
[iterates]: control-flow.html#the-iterator-protocol
|
||||
|
||||
If you want to see all of the key-value pairs in a map, the easiest way is to
|
||||
iterate over the keys and use each to look up its value:
|
||||
Regardless of how you iterate, the *order* that things are iterated in
|
||||
isn't defined. Wren makes no promises about what order keys and values are
|
||||
iterated. All it promises is that every entry will appear exactly once.
|
||||
|
||||
**Iterating with for(entry in map)**
|
||||
When you iterate a map with `for`, you'll be handed an _entry_, which contains
|
||||
a `key` and a `value` field. That gives you the info for each element in the map.
|
||||
|
||||
<pre class="snippet">
|
||||
var birds = {
|
||||
"Arizona": "Cactus wren",
|
||||
"Hawaii": "Nēnē",
|
||||
"Ohio": "Northern Cardinal"
|
||||
}
|
||||
|
||||
for (bird in birds) {
|
||||
System.print("The state bird of %(bird.key) is %(bird.value)")
|
||||
}
|
||||
</pre>
|
||||
|
||||
**Iterating using the keys**
|
||||
|
||||
You can also iterate over the keys and use each to look up its value:
|
||||
|
||||
<pre class="snippet">
|
||||
var birds = {
|
||||
@ -132,15 +158,10 @@ var birds = {
|
||||
}
|
||||
|
||||
for (state in birds.keys) {
|
||||
System.print("The state bird of " + state + " is " + birds[state])
|
||||
System.print("The state bird of %(state) is " + birds[state])
|
||||
}
|
||||
</pre>
|
||||
|
||||
This program prints the three states and their birds. However, the *order*
|
||||
that they are printed isn't defined. Wren makes no promises about what order
|
||||
keys and values are iterated in when you use these methods. All it promises is
|
||||
that every entry will appear exactly once.
|
||||
|
||||
<br><hr>
|
||||
<a class="right" href="method-calls.html">Method Calls →</a>
|
||||
<a href="lists.html">← Lists</a>
|
||||
|
||||
@ -52,7 +52,9 @@ In a language like Python or JavaScript, these would both call a single `int()`
|
||||
method, which has some kind of "optional" parameter. The body of the method
|
||||
figures out how many arguments were passed and uses control flow to handle the
|
||||
two different behaviors. That means first parameter represents "max unless
|
||||
another parameter was passed, in which case it's min". Kind of gross.
|
||||
another parameter was passed, in which case it's min".
|
||||
|
||||
This type of 'variadic' code isn't ideal, so Wren doesn't encourage it.
|
||||
|
||||
In Wren, these are calls to two entirely separate methods, `int(_,_)` and
|
||||
`int(_)`. This makes it easier to define "overloads" like this since you don't
|
||||
@ -163,7 +165,7 @@ like mocks or proxies where you want an object to masquerade as a certain class.
|
||||
|
||||
## Subscripts
|
||||
|
||||
Another familiar syntax from math class is *subscripting* using square brackets
|
||||
Another familiar syntax from math is *subscripting* using square brackets
|
||||
(`[]`). It's handy for working with collection-like objects. For example:
|
||||
|
||||
<pre class="snippet">
|
||||
|
||||
@ -42,6 +42,17 @@ if (thirsty) {
|
||||
}
|
||||
</pre>
|
||||
|
||||
If you need to import a variable under a different name, you can use
|
||||
`import "..." for Name as OtherName`. This looks up the top-level variable
|
||||
`Name` in *that* module, but declares a variable called `OtherName` in *this* module
|
||||
with its value.
|
||||
|
||||
<pre class="snippet">
|
||||
import "liquids" for Water //Water is now taken
|
||||
import "beverages" for Coffee, Water as H2O, Tea
|
||||
// var water = H2O.new()
|
||||
</pre>
|
||||
|
||||
If you want to load a module, but not bind any variables from it, you can omit
|
||||
the `for` clause:
|
||||
|
||||
@ -81,17 +92,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
|
||||
|
||||
|
||||
@ -174,14 +174,93 @@ System.print("Caught error: " + error)
|
||||
|
||||
If the called fiber raises an error, it can no longer be used.
|
||||
|
||||
### **try**(value)
|
||||
Tries to run the fiber. If a runtime error occurs
|
||||
in the called fiber, the error is captured and is returned as a string.
|
||||
If the fiber is being
|
||||
started for the first time, and its function takes a parameter, `value` is
|
||||
passed to it.
|
||||
|
||||
<pre class="snippet">
|
||||
var fiber = Fiber.new {|value|
|
||||
value.badMethod
|
||||
}
|
||||
|
||||
var error = fiber.try("just a string")
|
||||
System.print("Caught error: " + error)
|
||||
</pre>
|
||||
|
||||
If the called fiber raises an error, it can no longer be used.
|
||||
|
||||
### **transfer**()
|
||||
|
||||
**TODO**
|
||||
Pauses execution of the current running fiber, and transfers control to this fiber.
|
||||
|
||||
[Read more][transfers] about the difference between `call` and `transfer`.
|
||||
Unlike `call`, `transfer` doesn't track the origin of the transfer.
|
||||
|
||||
[transfers]: ../../concurrency.html#transferring-control
|
||||
|
||||
<pre class="snippet">
|
||||
// keep hold of the fiber we start in
|
||||
var main = Fiber.current
|
||||
|
||||
// create a new fiber, note it doesn't execute yet!
|
||||
var fiber = Fiber.new {
|
||||
System.print("inside 'fiber'") //> #2: from #1
|
||||
main.transfer() //> #3: go back to 'main'
|
||||
}
|
||||
|
||||
fiber.transfer() //> #1: print "inside 'fiber'" via #2
|
||||
//> this fiber is now paused by #1
|
||||
|
||||
System.print("main") //> #4: prints "main", unpaused by #3
|
||||
</pre>
|
||||
|
||||
### **transfer**(value)
|
||||
|
||||
**TODO**
|
||||
Pauses execution of the current running fiber, and transfers control to this fiber.
|
||||
|
||||
Similar to `transfer`, but a value can be passed between the fibers.
|
||||
|
||||
<pre class="snippet">
|
||||
// keep hold of the fiber we start in
|
||||
var main = Fiber.current
|
||||
|
||||
// create a new fiber, note it doesn't execute yet
|
||||
// also note that we're accepting a 'value' parameter
|
||||
var fiber = Fiber.new {|value|
|
||||
System.print("in 'fiber' = %(value)") //> #2: in 'fiber' = 5
|
||||
var result = main.transfer("hello?") //> #3: send to 'message'
|
||||
System.print("end 'fiber' = %(result)") //> #6: end 'fiber' = 32
|
||||
}
|
||||
|
||||
var message = fiber.transfer(5) //> #1: send to 'value'
|
||||
System.print("... %(message)") //> #4: ... hello?
|
||||
fiber.transfer(32) //> #5: send to 'result'
|
||||
</pre>
|
||||
|
||||
### **transferError**(error)
|
||||
|
||||
**TODO**
|
||||
Transfer to this fiber, but set this fiber into an error state.
|
||||
The `fiber.error` value will be populated with the value in `error`.
|
||||
|
||||
<pre class="snippet">
|
||||
var A = Fiber.new {
|
||||
System.print("transferred to A") //> #4
|
||||
B.transferError("error!") //> #5
|
||||
}
|
||||
|
||||
var B = Fiber.new {
|
||||
System.print("started B") //> #2
|
||||
A.transfer() //> #3
|
||||
System.print("should not get here")
|
||||
}
|
||||
|
||||
B.try() //> #1
|
||||
System.print(B.error) //> #6: prints "error!" from #5
|
||||
|
||||
// B fiber can no longer be used
|
||||
|
||||
B.call() //> #7: Cannot call an aborted fiber.
|
||||
</pre>
|
||||
@ -12,7 +12,7 @@ An indexable contiguous collection of elements. More details [here][lists].
|
||||
|
||||
Creates a new list with `size` elements, all set to `element`.
|
||||
|
||||
It is a runtime error if `size` is not a nonnegative integer.
|
||||
It is a runtime error if `size` is not a non-negative integer.
|
||||
|
||||
### List.**new**()
|
||||
|
||||
@ -22,7 +22,19 @@ Creates a new empty list. Equivalent to `[]`.
|
||||
|
||||
### **add**(item)
|
||||
|
||||
Appends `item` to the end of the list.
|
||||
Appends `item` to the end of the list. Returns the added item.
|
||||
|
||||
### **addAll**(other)
|
||||
|
||||
Appends each element of `other` in the same order to the end of the list. `other` must be [an iterable](../../control-flow.html#the-iterator-protocol).
|
||||
|
||||
<pre class="snippet">
|
||||
var list = [0, 1, 2, 3, 4]
|
||||
list.addAll([5, 6])
|
||||
System.print(list) //> [0, 1, 2, 3, 4, 5, 6]
|
||||
</pre>
|
||||
|
||||
Returns the added items.
|
||||
|
||||
### **clear**()
|
||||
|
||||
@ -32,6 +44,16 @@ Removes all elements from the list.
|
||||
|
||||
The number of elements in the list.
|
||||
|
||||
### **indexOf**(value)
|
||||
|
||||
Returns the index of `value` in the list, if found. If not found, returns -1.
|
||||
|
||||
<pre class="snippet">
|
||||
var list = [0, 1, 2, 3, 4]
|
||||
System.print(list.indexOf(3)) //> 3
|
||||
System.print(list.indexOf(20)) //> -1
|
||||
</pre>
|
||||
|
||||
### **insert**(index, item)
|
||||
|
||||
Inserts the `item` at `index` in the list.
|
||||
@ -74,6 +96,26 @@ list.
|
||||
|
||||
[iterator protocol]: ../../control-flow.html#the-iterator-protocol
|
||||
|
||||
### **remove**(value)
|
||||
|
||||
Removes the first value found in the list that matches the given `value`,
|
||||
using regular equality to compare them. All trailing elements
|
||||
are shifted up to fill in where the removed element was.
|
||||
|
||||
<pre class="snippet">
|
||||
var list = ["a", "b", "c", "d"]
|
||||
list.remove("b")
|
||||
System.print(list) //> [a, c, d]
|
||||
</pre>
|
||||
|
||||
Returns the removed value, if found.
|
||||
If the value is not found in the list, returns null.
|
||||
|
||||
<pre class="snippet">
|
||||
System.print(["a", "b", "c"].remove("b")) //> b
|
||||
System.print(["a", "b", "c"].remove("not found")) //> null
|
||||
</pre>
|
||||
|
||||
### **removeAt**(index)
|
||||
|
||||
Removes the element at `index`. If `index` is negative, it counts backwards
|
||||
@ -94,6 +136,40 @@ System.print(["a", "b", "c"].removeAt(1)) //> b
|
||||
|
||||
It is a runtime error if the index is not an integer or is out of bounds.
|
||||
|
||||
### **sort**(), **sort**(comparer)
|
||||
|
||||
Sorts the elements of a list in-place; altering the list. The default sort is implemented using the quicksort algorithm.
|
||||
|
||||
<pre class="snippet">
|
||||
var list = [4, 1, 3, 2].sort()
|
||||
System.print(list) //> [1, 2, 3, 4]
|
||||
</pre>
|
||||
|
||||
A comparison function `comparer` can be provided to customise the element sorting. The comparison function must return a boolean value specifying the order in which elements should appear in the list.
|
||||
|
||||
The comparison function accepts two arguments `a` and `b`, two values to compare, and must return a boolean indicating the inequality between the arguments. If the function returns true, the first argument `a` will appear before the second `b` in the sorted results.
|
||||
|
||||
A compare function like `{|a, b| true }` will always put `a` before `b`. The default compare function is `{|a, b| a < b }`.
|
||||
|
||||
<pre class="snippet">
|
||||
var list = [9, 6, 8, 7]
|
||||
list.sort {|a, b| a < b}
|
||||
System.print(list) //> [6, 7, 8, 9]
|
||||
</pre>
|
||||
|
||||
It is a runtime error if `comparer` is not a function.
|
||||
|
||||
### **swap**(index0, index1)
|
||||
|
||||
Swaps values inside the list around. Puts the value from `index0` in `index1`,
|
||||
and the value from `index1` at `index0` in the list.
|
||||
|
||||
<pre class="snippet">
|
||||
var list = [0, 1, 2, 3, 4]
|
||||
list.swap(0, 3)
|
||||
System.print(list) //> [3, 1, 2, 0, 4]
|
||||
</pre>
|
||||
|
||||
### **[**index**]** operator
|
||||
|
||||
Gets the element at `index`. If `index` is negative, it counts backwards from
|
||||
@ -104,7 +180,17 @@ var list = ["a", "b", "c"]
|
||||
System.print(list[1]) //> b
|
||||
</pre>
|
||||
|
||||
It is a runtime error if the index is not an integer or is out of bounds.
|
||||
If `index` is a [Range](range.html), a new list is populated from the elements
|
||||
in the range.
|
||||
|
||||
<pre class="snippet">
|
||||
var list = ["a", "b", "c"]
|
||||
System.print(list[0..1]) //> [a, b]
|
||||
</pre>
|
||||
|
||||
You can use `list[0..-1]` to shallow-copy a list.
|
||||
|
||||
It is a runtime error if the index is not an integer or range, or is out of bounds.
|
||||
|
||||
### **[**index**]=**(item) operator
|
||||
|
||||
@ -119,13 +205,23 @@ System.print(list) //> [a, new, c]
|
||||
|
||||
It is a runtime error if the index is not an integer or is out of bounds.
|
||||
|
||||
## **+**(other) operator
|
||||
### **+**(other) operator
|
||||
|
||||
Appends a list to the end of the list (concatenation). `other` must be a `List`.
|
||||
Appends a list to the end of the list (concatenation). `other` must be [an iterable](../../control-flow.html#the-iterator-protocol).
|
||||
|
||||
<pre class="snippet">
|
||||
var letters = ["a", "b", "c"]
|
||||
var other = ["d", "e", "f"]
|
||||
var combined = letters + other
|
||||
System.print(combined) //> [a, b, c, d, e, f]
|
||||
</pre>
|
||||
</pre>
|
||||
|
||||
### **\***(count) operator
|
||||
|
||||
Creates a new list by repeating this one ```count``` times. It is a runtime error if ```count``` is not a non-negative integer.
|
||||
|
||||
<pre class="snippet">
|
||||
var digits = [1, 2]
|
||||
var tripleDigits = digits * 3
|
||||
System.print(tripleDigits) //> [1, 2, 1, 2, 1, 2]
|
||||
</pre>
|
||||
|
||||
@ -1,7 +1,15 @@
|
||||
^title Map Class
|
||||
|
||||
Extends [Sequence](sequence.html).
|
||||
|
||||
An associative collection that maps keys to values. More details [here](../../maps.html).
|
||||
|
||||
## Static Method
|
||||
|
||||
### Map.**new**()
|
||||
|
||||
Creates a new empty map. Equivalent to `{}`.
|
||||
|
||||
## Methods
|
||||
|
||||
### **clear**()
|
||||
@ -56,3 +64,22 @@ replaces the previous association.
|
||||
It is a runtime error if the key is not a [Bool](bool.html),
|
||||
[Class](class.html), [Null](null.html), [Num](num.html), [Range](range.html),
|
||||
or [String](string.html).
|
||||
|
||||
### **iterate**(iterator), **iteratorValue**(iterator)
|
||||
|
||||
Implements the [iterator protocol][] for iterating over the keys and values of a map at the same time.
|
||||
|
||||
[iterator protocol]: ../../control-flow.html#the-iterator-protocol
|
||||
|
||||
When a map (as opposed to its keys or values separately) is iterated over, each key/value pair is wrapped in a `MapEntry` object. `MapEntry` is a small helper class which has read-only `key` and `value` properties and a familiar `toString` representation.
|
||||
|
||||
<pre class="snippet">
|
||||
var map = {"paul": "mccartney"}
|
||||
for (entry in map) {
|
||||
System.print(entry.type) // MapEntry
|
||||
System.print(entry.key + " " + entry.value) // paul mccartney
|
||||
System.print(entry) // paul:mccartney
|
||||
}
|
||||
</pre>
|
||||
|
||||
All map entries will be iterated over, but may be in any order, and may even change between invocations of Wren.
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
### **!** operator
|
||||
|
||||
Returns `true`, since `null` is considered [false](../control-flow.html#truth).
|
||||
Returns `true`, since `null` is considered [false](../../control-flow.html#truth).
|
||||
|
||||
<pre class="snippet">
|
||||
System.print(!null) //> true
|
||||
|
||||
@ -9,10 +9,24 @@ Attempts to parse `value` as a decimal literal and return it as an instance of
|
||||
|
||||
It is a runtime error if `value` is not a string.
|
||||
|
||||
### Num.**infinity**
|
||||
|
||||
The value of ∞.
|
||||
|
||||
### Num.**nan**
|
||||
|
||||
One value representing a NaN.
|
||||
|
||||
Provides a default NaN number suitable for the vm internal values.
|
||||
|
||||
### Num.**pi**
|
||||
|
||||
The value of π.
|
||||
|
||||
### Num.**tau**
|
||||
|
||||
The value of τ. This is equivalent to ```2 * Num.pi```.
|
||||
|
||||
### Num.**largest**
|
||||
|
||||
The largest representable numeric value.
|
||||
@ -21,6 +35,17 @@ The largest representable numeric value.
|
||||
|
||||
The smallest positive representable numeric value.
|
||||
|
||||
### Num.**maxSafeInteger**
|
||||
|
||||
The largest integer that Wren can safely represent. It's a constant value of `9007199254740991`.
|
||||
|
||||
This is relevant because Wren uses double precision [floating-point format](https://en.wikipedia.org/wiki/IEEE_floating_point)
|
||||
for numbers, which can only safely represent integers between <code>-(2<sup>53</sup> - 1)</code> and <code>2<sup>53</sup> - 1</code>.
|
||||
|
||||
### Num.**minSafeInteger**
|
||||
|
||||
The smallest integer Wren can safely represent. It's a constant value of `-9007199254740991`.
|
||||
|
||||
## Methods
|
||||
|
||||
### **abs**
|
||||
@ -48,6 +73,10 @@ The arc tangent of the number.
|
||||
The arc tangent of the number when divided by `x`, using the signs of the two
|
||||
numbers to determine the quadrant of the result.
|
||||
|
||||
### **cbrt**
|
||||
|
||||
The cube root of the number.
|
||||
|
||||
### **ceil**
|
||||
|
||||
Rounds the number up to the nearest integer.
|
||||
@ -70,6 +99,17 @@ System.print(1.5.floor) //> 1
|
||||
System.print((-3.2).floor) //> -4
|
||||
</pre>
|
||||
|
||||
### **fraction**
|
||||
|
||||
The fractional part of a number i.e. the part after any decimal point.
|
||||
|
||||
The returned value has the same sign as `this`.
|
||||
|
||||
<pre class="snippet">
|
||||
System.print(1.5.fraction) //> 0.5
|
||||
System.print((-3.2).fraction) //> -0.2
|
||||
</pre>
|
||||
|
||||
### **isInfinity**
|
||||
|
||||
Whether the number is positive or negative infinity or not.
|
||||
@ -96,7 +136,29 @@ 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ⁿ`.
|
||||
|
||||
### **min**(other)
|
||||
|
||||
Returns the minimum value when comparing this number and `other`.
|
||||
|
||||
### **max**(other)
|
||||
|
||||
Returns the maximum value when comparing this number and `other`.
|
||||
|
||||
### **clamp**(min, max)
|
||||
|
||||
Clamps a number into the range of `min` and `max`. If this number is less than min,
|
||||
`min` is returned. If bigger than `max`, `max` is returned. Otherwise, the number
|
||||
itself is returned.
|
||||
|
||||
### **pow**(power)
|
||||
|
||||
@ -128,6 +190,21 @@ The square root of the number. Returns `nan` if the number is negative.
|
||||
|
||||
The tangent of the number.
|
||||
|
||||
### **toString**
|
||||
|
||||
The string representation of the number.
|
||||
|
||||
### **truncate**
|
||||
|
||||
Rounds the number to the nearest integer towards zero.
|
||||
|
||||
It is therefore equivalent to `floor` if the number is non-negative or `ceil` if it is negative.
|
||||
|
||||
<pre class="snippet">
|
||||
System.print(1.5.truncate) //> 1
|
||||
System.print((-3.2).truncate) //> -3
|
||||
</pre>
|
||||
|
||||
### **-** operator
|
||||
|
||||
Negates the number.
|
||||
@ -179,6 +256,24 @@ unsigned values. The result is then a 32-bit unsigned number where each bit is
|
||||
|
||||
It is a runtime error if `other` is not a number.
|
||||
|
||||
### **^**(other) operator
|
||||
|
||||
Performs bitwise exclusive or on the number. Both numbers are first converted to 32-bit unsigned values. The result is then a 32-bit unsigned number where each bit is `true` only where the corresponding bits of one (but not both) inputs were `true`. Each bit is therefore `false` if the corresponding bits of both inputs were either both `true` or both `false`.
|
||||
|
||||
It is a runtime error if `other` is not a number.
|
||||
|
||||
### **<<**(other) operator
|
||||
|
||||
Performs a bitwise left shift on the number. Internally, both numbers are first converted to 32-bit unsigned values and C's left shift operator is then applied to them.
|
||||
|
||||
It is a runtime error if `other` is not a number.
|
||||
|
||||
### **>>**(other) operator
|
||||
|
||||
Performs a bitwise right shift on the number. Internally, both numbers are first converted to 32-bit unsigned values and C's right shift operator is then applied to them.
|
||||
|
||||
It is a runtime error if `other` is not a number.
|
||||
|
||||
### **..**(other) operator
|
||||
|
||||
Creates a [Range](range.html) representing a consecutive range of numbers
|
||||
@ -201,4 +296,4 @@ var range = 1.2...3.4
|
||||
System.print(range.min) //> 1.2
|
||||
System.print(range.max) //> 3.4
|
||||
System.print(range.isInclusive) //> false
|
||||
</pre>
|
||||
</pre>
|
||||
|
||||
@ -40,6 +40,15 @@ var hPosition = metalBand.indexOf("h")
|
||||
System.print(metalBand[hPosition]) //> h
|
||||
</pre>
|
||||
|
||||
A string can also be indexed with a [Range](range.html), which will return a
|
||||
new string as a substring of the original.
|
||||
|
||||
<pre class="snippet">
|
||||
var example = "hello wren"
|
||||
System.print(example[0...5]) //> hello
|
||||
System.print(example[-4..-1]) //> wren
|
||||
</pre>
|
||||
|
||||
If you want to work with a string as a sequence numeric code points, call the
|
||||
`codePoints` getter. It returns a [Sequence](sequence.html) that decodes UTF-8
|
||||
and iterates over the code points, returning each as a number.
|
||||
@ -246,6 +255,12 @@ Returns a new string that concatenates this string and `other`.
|
||||
|
||||
It is a runtime error if `other` is not a string.
|
||||
|
||||
### *****(count) operator
|
||||
|
||||
Returns a new string that contains this string repeated `count` times.
|
||||
|
||||
It is a runtime error if `count` is not a positive integer.
|
||||
|
||||
### **==**(other) operator
|
||||
|
||||
Checks if the string is equal to `other`.
|
||||
|
||||
@ -48,3 +48,8 @@ System.write(4 + 5) //> 9
|
||||
|
||||
In the above example, the result of `4 + 5` is printed, and then the prompt is
|
||||
printed on the same line because no newline character was printed afterwards.
|
||||
|
||||
### System.**writeAll**(sequence)
|
||||
|
||||
Iterates over `sequence` and prints each element, but does not print a newline
|
||||
character afterwards. Each element is converted to a string by calling `toString` on it.
|
||||
|
||||
@ -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
@ -607,8 +607,8 @@ var wasmMemory;
|
||||
// In the wasm backend, we polyfill the WebAssembly object,
|
||||
// so this creates a (non-native-wasm) table for us.
|
||||
var wasmTable = new WebAssembly.Table({
|
||||
'initial': 191,
|
||||
'maximum': 191 + 0,
|
||||
'initial': 203,
|
||||
'maximum': 203 + 0,
|
||||
'element': 'anyfunc'
|
||||
});
|
||||
|
||||
@ -1229,11 +1229,11 @@ function updateGlobalBufferAndViews(buf) {
|
||||
}
|
||||
|
||||
var STATIC_BASE = 1024,
|
||||
STACK_BASE = 5271824,
|
||||
STACK_BASE = 5271568,
|
||||
STACKTOP = STACK_BASE,
|
||||
STACK_MAX = 28944,
|
||||
DYNAMIC_BASE = 5271824,
|
||||
DYNAMICTOP_PTR = 28784;
|
||||
STACK_MAX = 28688,
|
||||
DYNAMIC_BASE = 5271568,
|
||||
DYNAMICTOP_PTR = 28528;
|
||||
|
||||
assert(STACK_BASE % 16 === 0, 'stack must start aligned');
|
||||
assert(DYNAMIC_BASE % 16 === 0, 'heap must start aligned');
|
||||
@ -1817,7 +1817,7 @@ var ASM_CONSTS = {
|
||||
|
||||
|
||||
|
||||
// STATICTOP = STATIC_BASE + 27920;
|
||||
// STATICTOP = STATIC_BASE + 27664;
|
||||
/* global initializers */ __ATINIT__.push({ func: function() { ___wasm_call_ctors() } });
|
||||
|
||||
|
||||
@ -1869,12 +1869,36 @@ var ASM_CONSTS = {
|
||||
abort('stack overflow')
|
||||
}
|
||||
|
||||
|
||||
function ___setErrNo(value) {
|
||||
if (Module['___errno_location']) HEAP32[((Module['___errno_location']())>>2)]=value;
|
||||
else err('failed to set errno from JS');
|
||||
return value;
|
||||
function _clock() {
|
||||
if (_clock.start === undefined) _clock.start = Date.now();
|
||||
return ((Date.now() - _clock.start) * (1000000 / 1000))|0;
|
||||
}
|
||||
|
||||
function _emscripten_get_sbrk_ptr() {
|
||||
return 28528;
|
||||
}
|
||||
|
||||
function _emscripten_memcpy_big(dest, src, num) {
|
||||
HEAPU8.copyWithin(dest, src, src + num);
|
||||
}
|
||||
|
||||
|
||||
function _emscripten_get_heap_size() {
|
||||
return HEAPU8.length;
|
||||
}
|
||||
|
||||
function abortOnCannotGrowMemory(requestedSize) {
|
||||
abort('Cannot enlarge memory arrays to size ' + requestedSize + ' bytes (OOM). Either (1) compile with -s INITIAL_MEMORY=X with X higher than the current value ' + HEAP8.length + ', (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ');
|
||||
}function _emscripten_resize_heap(requestedSize) {
|
||||
abortOnCannotGrowMemory(requestedSize);
|
||||
}
|
||||
|
||||
function _exit(status) {
|
||||
// void _exit(int status);
|
||||
// http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html
|
||||
exit(status);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var PATH={splitPath:function(filename) {
|
||||
@ -1963,62 +1987,11 @@ var ASM_CONSTS = {
|
||||
if (low >= 0) assert(high === 0);
|
||||
else assert(high === -1);
|
||||
return low;
|
||||
}};function ___sys_fcntl64(fd, cmd, varargs) {SYSCALLS.varargs = varargs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function ___sys_ioctl(fd, op, varargs) {SYSCALLS.varargs = varargs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function ___sys_open(path, flags, varargs) {SYSCALLS.varargs = varargs;
|
||||
|
||||
abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM')}
|
||||
|
||||
function _clock() {
|
||||
if (_clock.start === undefined) _clock.start = Date.now();
|
||||
return ((Date.now() - _clock.start) * (1000000 / 1000))|0;
|
||||
}
|
||||
|
||||
function _emscripten_get_sbrk_ptr() {
|
||||
return 28784;
|
||||
}
|
||||
|
||||
function _emscripten_memcpy_big(dest, src, num) {
|
||||
HEAPU8.copyWithin(dest, src, src + num);
|
||||
}
|
||||
|
||||
|
||||
function _emscripten_get_heap_size() {
|
||||
return HEAPU8.length;
|
||||
}
|
||||
|
||||
function abortOnCannotGrowMemory(requestedSize) {
|
||||
abort('Cannot enlarge memory arrays to size ' + requestedSize + ' bytes (OOM). Either (1) compile with -s INITIAL_MEMORY=X with X higher than the current value ' + HEAP8.length + ', (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ');
|
||||
}function _emscripten_resize_heap(requestedSize) {
|
||||
abortOnCannotGrowMemory(requestedSize);
|
||||
}
|
||||
|
||||
function _exit(status) {
|
||||
// void _exit(int status);
|
||||
// http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html
|
||||
exit(status);
|
||||
}
|
||||
|
||||
function _fd_close(fd) {
|
||||
}};function _fd_close(fd) {
|
||||
abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM');
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _fd_read(fd, iov, iovcnt, pnum) {
|
||||
var stream = SYSCALLS.getStreamFromFD(fd);
|
||||
var num = SYSCALLS.doReadv(stream, iov, iovcnt);
|
||||
HEAP32[((pnum)>>2)]=num
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {
|
||||
abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM')}
|
||||
|
||||
@ -2097,7 +2070,7 @@ function intArrayToString(array) {
|
||||
// ASM_LIBRARY EXTERN PRIMITIVES: Math_floor,Math_ceil
|
||||
|
||||
var asmGlobalArg = {};
|
||||
var asmLibraryArg = { "__handle_stack_overflow": ___handle_stack_overflow, "__sys_fcntl64": ___sys_fcntl64, "__sys_ioctl": ___sys_ioctl, "__sys_open": ___sys_open, "clock": _clock, "emscripten_get_sbrk_ptr": _emscripten_get_sbrk_ptr, "emscripten_memcpy_big": _emscripten_memcpy_big, "emscripten_resize_heap": _emscripten_resize_heap, "exit": _exit, "fd_close": _fd_close, "fd_read": _fd_read, "fd_seek": _fd_seek, "fd_write": _fd_write, "memory": wasmMemory, "round": _round, "setTempRet0": _setTempRet0, "table": wasmTable, "time": _time };
|
||||
var asmLibraryArg = { "__handle_stack_overflow": ___handle_stack_overflow, "clock": _clock, "emscripten_get_sbrk_ptr": _emscripten_get_sbrk_ptr, "emscripten_memcpy_big": _emscripten_memcpy_big, "emscripten_resize_heap": _emscripten_resize_heap, "exit": _exit, "fd_close": _fd_close, "fd_seek": _fd_seek, "fd_write": _fd_write, "memory": wasmMemory, "round": _round, "setTempRet0": _setTempRet0, "table": wasmTable, "time": _time };
|
||||
var asm = createWasm();
|
||||
Module["asm"] = asm;
|
||||
/** @type {function(...*):?} */
|
||||
@ -2185,10 +2158,10 @@ var dynCall_vii = Module["dynCall_vii"] = function() {
|
||||
};
|
||||
|
||||
/** @type {function(...*):?} */
|
||||
var dynCall_iii = Module["dynCall_iii"] = function() {
|
||||
var dynCall_viii = Module["dynCall_viii"] = function() {
|
||||
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
|
||||
assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
|
||||
return Module["asm"]["dynCall_iii"].apply(null, arguments)
|
||||
return Module["asm"]["dynCall_viii"].apply(null, arguments)
|
||||
};
|
||||
|
||||
/** @type {function(...*):?} */
|
||||
@ -2198,6 +2171,13 @@ var dynCall_iiii = Module["dynCall_iiii"] = function() {
|
||||
return Module["asm"]["dynCall_iiii"].apply(null, arguments)
|
||||
};
|
||||
|
||||
/** @type {function(...*):?} */
|
||||
var dynCall_iii = Module["dynCall_iii"] = function() {
|
||||
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
|
||||
assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
|
||||
return Module["asm"]["dynCall_iii"].apply(null, arguments)
|
||||
};
|
||||
|
||||
/** @type {function(...*):?} */
|
||||
var dynCall_vi = Module["dynCall_vi"] = function() {
|
||||
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
|
||||
@ -2205,13 +2185,6 @@ var dynCall_vi = Module["dynCall_vi"] = function() {
|
||||
return Module["asm"]["dynCall_vi"].apply(null, arguments)
|
||||
};
|
||||
|
||||
/** @type {function(...*):?} */
|
||||
var dynCall_jiji = Module["dynCall_jiji"] = function() {
|
||||
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
|
||||
assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
|
||||
return Module["asm"]["dynCall_jiji"].apply(null, arguments)
|
||||
};
|
||||
|
||||
/** @type {function(...*):?} */
|
||||
var dynCall_ii = Module["dynCall_ii"] = function() {
|
||||
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
|
||||
@ -2219,6 +2192,13 @@ var dynCall_ii = Module["dynCall_ii"] = function() {
|
||||
return Module["asm"]["dynCall_ii"].apply(null, arguments)
|
||||
};
|
||||
|
||||
/** @type {function(...*):?} */
|
||||
var dynCall_jiji = Module["dynCall_jiji"] = function() {
|
||||
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
|
||||
assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
|
||||
return Module["asm"]["dynCall_jiji"].apply(null, arguments)
|
||||
};
|
||||
|
||||
/** @type {function(...*):?} */
|
||||
var dynCall_iidiiii = Module["dynCall_iidiiii"] = function() {
|
||||
assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
|
||||
|
||||
Binary file not shown.
@ -43,7 +43,7 @@ One way to get a quick feel for a language's style is to see what words it
|
||||
reserves. Here's what Wren has:
|
||||
|
||||
<pre class="snippet">
|
||||
break class construct else false for foreign if import
|
||||
as break class construct continue else false for foreign if import
|
||||
in is null return static super this true var while
|
||||
</pre>
|
||||
|
||||
@ -149,7 +149,7 @@ put a newline in there:
|
||||
</pre>
|
||||
|
||||
Using an initial newline after the `{` does feel a little weird or magical, but
|
||||
newlines are already significant in Wren, so it's not totally crazy. The nice
|
||||
newlines are already significant in Wren, so it's not totally unreasonable. The nice
|
||||
thing about this syntax as opposed to something like `=>` is that the *end* of
|
||||
the block has an explicit delimiter. That helps when chaining:
|
||||
|
||||
|
||||
@ -41,8 +41,8 @@
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
@ -90,8 +90,8 @@
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
@ -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>
|
||||
|
||||
@ -42,8 +42,8 @@
|
||||
<li><a href="../method-calls.html">Method Calls</a></li>
|
||||
<li><a href="../control-flow.html">Control Flow</a></li>
|
||||
<li><a href="../variables.html">Variables</a></li>
|
||||
<li><a href="../functions.html">Functions</a></li>
|
||||
<li><a href="../classes.html">Classes</a></li>
|
||||
<li><a href="../functions.html">Functions</a></li>
|
||||
<li><a href="../concurrency.html">Concurrency</a></li>
|
||||
<li><a href="../error-handling.html">Error Handling</a></li>
|
||||
<li><a href="../modularity.html">Modularity</a></li>
|
||||
@ -91,8 +91,8 @@
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="../variables.html">Variables</a></li>
|
||||
<li><a href="../functions.html">Functions</a></li>
|
||||
<li><a href="../classes.html">Classes</a></li>
|
||||
<li><a href="../functions.html">Functions</a></li>
|
||||
<li><a href="../concurrency.html">Concurrency</a></li>
|
||||
<li><a href="../error-handling.html">Error Handling</a></li>
|
||||
<li><a href="../modularity.html">Modularity</a></li>
|
||||
@ -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,10 @@ from other languages:
|
||||
3.14159
|
||||
1.0
|
||||
-12.34
|
||||
0.0314159e02
|
||||
0.0314159e+02
|
||||
314.159e-02
|
||||
0xcaffe2
|
||||
</pre>
|
||||
|
||||
Numbers are instances of the [Num][] class.
|
||||
@ -45,6 +50,16 @@ String literals are surrounded in double quotes:
|
||||
"hi there"
|
||||
</pre>
|
||||
|
||||
They can also span multiple lines:
|
||||
|
||||
<pre class="snippet">
|
||||
"hi
|
||||
there,
|
||||
again"
|
||||
</pre>
|
||||
|
||||
### Escaping
|
||||
|
||||
A handful of escape characters are supported:
|
||||
|
||||
<pre class="snippet">
|
||||
@ -54,11 +69,23 @@ A handful of escape characters are supported:
|
||||
"\%" // A percent sign.
|
||||
"\a" // Alarm beep. (Who uses this?)
|
||||
"\b" // Backspace.
|
||||
"\e" // ESC character.
|
||||
"\f" // Formfeed.
|
||||
"\n" // Newline.
|
||||
"\r" // Carriage return.
|
||||
"\t" // Tab.
|
||||
"\v" // Vertical tab.
|
||||
|
||||
|
||||
"\x48" // Unencoded byte (2 hex digits)
|
||||
"\u0041" // Unicode code point (4 hex digits)
|
||||
"\U0001F64A" // Unicode code point (8 hex digits)
|
||||
</pre>
|
||||
|
||||
A `\x` followed by two hex digits specifies a single unencoded byte:
|
||||
|
||||
<pre class="snippet">
|
||||
System.print("\x48\x69\x2e") //> Hi.
|
||||
</pre>
|
||||
|
||||
A `\u` followed by four hex digits can be used to specify a Unicode code point:
|
||||
@ -74,12 +101,6 @@ of the basic multilingual plane, like all-important emoji:
|
||||
System.print("\U0001F64A\U0001F680") //> 🙊🚀
|
||||
</pre>
|
||||
|
||||
A `\x` followed by two hex digits specifies a single unencoded byte:
|
||||
|
||||
<pre class="snippet">
|
||||
System.print("\x48\x69\x2e") //> Hi.
|
||||
</pre>
|
||||
|
||||
Strings are instances of class [String][].
|
||||
|
||||
[string]: modules/core/string.html
|
||||
@ -104,6 +125,63 @@ System.print("wow %((1..3).map {|n| n * n}.join())") //> wow 149
|
||||
An interpolated expression can even contain a string literal which in turn has
|
||||
its own nested interpolations, but doing that gets unreadable pretty quickly.
|
||||
|
||||
### Raw strings
|
||||
|
||||
A string literal can also be created using triple quotes `"""` which is
|
||||
parsed as a raw string. A raw string is no different
|
||||
from any other string, it's just parsed in a different way.
|
||||
|
||||
**Raw strings do not process escapes and do not apply any interpolation**.
|
||||
|
||||
<pre class="snippet">
|
||||
"""hi there"""
|
||||
</pre>
|
||||
|
||||
When a raw string spans multiple lines and a triple quote is on it's own line,
|
||||
any whitespace on that line will be ignored. This means the opening and closing
|
||||
lines are not counted as part of the string when the triple quotes are separate lines,
|
||||
as long as they only contain whitespace (spaces + tabs).
|
||||
|
||||
<pre class="snippet">
|
||||
"""
|
||||
Hello world
|
||||
"""
|
||||
</pre>
|
||||
|
||||
The resulting value in the string above has no newlines or trailing whitespace.
|
||||
Note the spaces in front of the Hello are preserved.
|
||||
|
||||
<pre class="snippet">
|
||||
Hello world
|
||||
</pre>
|
||||
|
||||
A raw string will be parsed exactly as is in the file, unmodified.
|
||||
This means it can contain quotes, invalid syntax, other data formats
|
||||
and so on without being modified by Wren.
|
||||
|
||||
<pre class="snippet">
|
||||
"""
|
||||
{
|
||||
"hello": "wren",
|
||||
"from" : "json"
|
||||
}
|
||||
"""
|
||||
</pre>
|
||||
|
||||
One more example, embedding wren code inside a string safely.
|
||||
|
||||
<pre class="snippet">
|
||||
"""
|
||||
A markdown string with embedded wren code example.
|
||||
|
||||
class Example {
|
||||
construct code() {
|
||||
//
|
||||
}
|
||||
}
|
||||
"""
|
||||
</pre>
|
||||
|
||||
## Ranges
|
||||
|
||||
A range is a little object that represents a consecutive range of numbers. They
|
||||
@ -127,12 +205,16 @@ This creates a range from four to six *not* including six itself. Ranges are
|
||||
commonly used for [iterating](control-flow.html#for-statements) over a
|
||||
sequences of numbers, but are useful in other places too. You can pass them to
|
||||
a [list](lists.html)'s subscript operator to return a subset of the list, for
|
||||
example:
|
||||
example, or on a String, the substring in that range:
|
||||
|
||||
<pre class="snippet">
|
||||
var list = ["a", "b", "c", "d", "e"]
|
||||
var slice = list[1..3]
|
||||
System.print(slice) //> [b, c, d]
|
||||
|
||||
var string = "hello wren"
|
||||
var wren = string[-4..-1]
|
||||
System.print(wren) //> wren
|
||||
</pre>
|
||||
|
||||
Their class is [Range][].
|
||||
|
||||
@ -31,7 +31,7 @@ System.print(a) //! "a" doesn't exist anymore.
|
||||
</pre>
|
||||
|
||||
Variables defined at the top level of a script are *top-level* and are visible
|
||||
to the [module](modules.html) system. All other variables are *local*.
|
||||
to the [module](modularity.html) system. All other variables are *local*.
|
||||
Declaring a variable in an inner scope with the same name as an outer one is
|
||||
called *shadowing* and is not an error (although it's not something you likely
|
||||
intend to do much).
|
||||
@ -54,7 +54,7 @@ var a = "again" //! "a" is already declared.
|
||||
|
||||
## Assignment
|
||||
|
||||
After a variable has been declared, you can assign to it using `=`:
|
||||
After a variable has been declared, you can assign to it using `=`
|
||||
|
||||
<pre class="snippet">
|
||||
var a = 123
|
||||
|
||||
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 */,
|
||||
|
||||
@ -7,18 +7,26 @@
|
||||
|
||||
// The Wren semantic version number components.
|
||||
#define WREN_VERSION_MAJOR 0
|
||||
#define WREN_VERSION_MINOR 3
|
||||
#define WREN_VERSION_MINOR 4
|
||||
#define WREN_VERSION_PATCH 0
|
||||
|
||||
// A human-friendly string representation of the version.
|
||||
#define WREN_VERSION_STRING "0.3.0"
|
||||
#define WREN_VERSION_STRING "0.4.0"
|
||||
|
||||
// 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)
|
||||
|
||||
#ifndef WREN_API
|
||||
#if defined(_MSC_VER) && defined(WREN_API_DLLEXPORT)
|
||||
#define WREN_API __declspec( dllexport )
|
||||
#else
|
||||
#define WREN_API
|
||||
#endif
|
||||
#endif //WREN_API
|
||||
|
||||
// A single virtual machine for executing Wren code.
|
||||
//
|
||||
// Wren has no global state, so all state stored by a running interpreter lives
|
||||
@ -47,7 +55,7 @@ typedef struct WrenHandle WrenHandle;
|
||||
//
|
||||
// - To free memory, [memory] will be the memory to free and [newSize] will be
|
||||
// zero. It should return NULL.
|
||||
typedef void* (*WrenReallocateFn)(void* memory, size_t newSize);
|
||||
typedef void* (*WrenReallocateFn)(void* memory, size_t newSize, void* userData);
|
||||
|
||||
// A function callable from Wren code, but implemented in C.
|
||||
typedef void (*WrenForeignMethodFn)(WrenVM* vm);
|
||||
@ -65,8 +73,25 @@ typedef void (*WrenFinalizerFn)(void* data);
|
||||
typedef const char* (*WrenResolveModuleFn)(WrenVM* vm,
|
||||
const char* importer, const char* name);
|
||||
|
||||
// Forward declare
|
||||
struct WrenLoadModuleResult;
|
||||
|
||||
// Called after loadModuleFn is called for module [name]. The original returned result
|
||||
// is handed back to you in this callback, so that you can free memory if appropriate.
|
||||
typedef void (*WrenLoadModuleCompleteFn)(WrenVM* vm, const char* name, struct WrenLoadModuleResult result);
|
||||
|
||||
// The result of a loadModuleFn call.
|
||||
// [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].
|
||||
@ -163,9 +188,9 @@ typedef struct
|
||||
// Since Wren does not talk directly to the file system, it relies on the
|
||||
// embedder to physically locate and read the source code for a module. The
|
||||
// first time an import appears, Wren will call this and pass in the name of
|
||||
// the module being imported. The VM should return the soure code for that
|
||||
// module. Memory for the source should be allocated using [reallocateFn] and
|
||||
// Wren will take ownership over it.
|
||||
// the module being imported. The method will return a result, which contains
|
||||
// the source code for that module. Memory for the source is owned by the
|
||||
// host application, and can be freed using the onComplete callback.
|
||||
//
|
||||
// This will only be called once for any given module name. Wren caches the
|
||||
// result internally so subsequent imports of the same module will use the
|
||||
@ -263,6 +288,7 @@ typedef enum
|
||||
WREN_TYPE_NUM,
|
||||
WREN_TYPE_FOREIGN,
|
||||
WREN_TYPE_LIST,
|
||||
WREN_TYPE_MAP,
|
||||
WREN_TYPE_NULL,
|
||||
WREN_TYPE_STRING,
|
||||
|
||||
@ -270,27 +296,32 @@ typedef enum
|
||||
WREN_TYPE_UNKNOWN
|
||||
} WrenType;
|
||||
|
||||
// Get the current wren version number.
|
||||
//
|
||||
// Can be used to range checks over versions.
|
||||
WREN_API int wrenGetVersionNumber();
|
||||
|
||||
// Initializes [configuration] with all of its default values.
|
||||
//
|
||||
// Call this before setting the particular fields you care about.
|
||||
void wrenInitConfiguration(WrenConfiguration* configuration);
|
||||
WREN_API void wrenInitConfiguration(WrenConfiguration* configuration);
|
||||
|
||||
// Creates a new Wren virtual machine using the given [configuration]. Wren
|
||||
// will copy the configuration data, so the argument passed to this can be
|
||||
// freed after calling this. If [configuration] is `NULL`, uses a default
|
||||
// configuration.
|
||||
WrenVM* wrenNewVM(WrenConfiguration* configuration);
|
||||
WREN_API WrenVM* wrenNewVM(WrenConfiguration* configuration);
|
||||
|
||||
// Disposes of all resources is use by [vm], which was previously created by a
|
||||
// call to [wrenNewVM].
|
||||
void wrenFreeVM(WrenVM* vm);
|
||||
WREN_API void wrenFreeVM(WrenVM* vm);
|
||||
|
||||
// Immediately run the garbage collector to free unused memory.
|
||||
void wrenCollectGarbage(WrenVM* vm);
|
||||
WREN_API void wrenCollectGarbage(WrenVM* vm);
|
||||
|
||||
// Runs [source], a string of Wren source code in a new fiber in [vm] in the
|
||||
// context of resolved [module].
|
||||
WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
|
||||
WREN_API WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
|
||||
const char* source);
|
||||
|
||||
// Creates a handle that can be used to invoke a method with [signature] on
|
||||
@ -301,7 +332,7 @@ WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
|
||||
//
|
||||
// When you are done with this handle, it must be released using
|
||||
// [wrenReleaseHandle].
|
||||
WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
|
||||
WREN_API WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
|
||||
|
||||
// Calls [method], using the receiver and arguments previously set up on the
|
||||
// stack.
|
||||
@ -313,11 +344,11 @@ WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
|
||||
// signature.
|
||||
//
|
||||
// After this returns, you can access the return value from slot 0 on the stack.
|
||||
WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
|
||||
WREN_API WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
|
||||
|
||||
// Releases the reference stored in [handle]. After calling this, [handle] can
|
||||
// no longer be used.
|
||||
void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
|
||||
WREN_API void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
|
||||
|
||||
// The following functions are intended to be called from foreign methods or
|
||||
// finalizers. The interface Wren provides to a foreign method is like a
|
||||
@ -357,7 +388,7 @@ void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
|
||||
// return, you get a very fast FFI.
|
||||
|
||||
// Returns the number of slots available to the current foreign method.
|
||||
int wrenGetSlotCount(WrenVM* vm);
|
||||
WREN_API int wrenGetSlotCount(WrenVM* vm);
|
||||
|
||||
// Ensures that the foreign method stack has at least [numSlots] available for
|
||||
// use, growing the stack if needed.
|
||||
@ -365,15 +396,15 @@ int wrenGetSlotCount(WrenVM* vm);
|
||||
// Does not shrink the stack if it has more than enough slots.
|
||||
//
|
||||
// It is an error to call this from a finalizer.
|
||||
void wrenEnsureSlots(WrenVM* vm, int numSlots);
|
||||
WREN_API void wrenEnsureSlots(WrenVM* vm, int numSlots);
|
||||
|
||||
// Gets the type of the object in [slot].
|
||||
WrenType wrenGetSlotType(WrenVM* vm, int slot);
|
||||
WREN_API WrenType wrenGetSlotType(WrenVM* vm, int slot);
|
||||
|
||||
// Reads a boolean value from [slot].
|
||||
//
|
||||
// It is an error to call this if the slot does not contain a boolean value.
|
||||
bool wrenGetSlotBool(WrenVM* vm, int slot);
|
||||
WREN_API bool wrenGetSlotBool(WrenVM* vm, int slot);
|
||||
|
||||
// Reads a byte array from [slot].
|
||||
//
|
||||
@ -385,19 +416,19 @@ bool wrenGetSlotBool(WrenVM* vm, int slot);
|
||||
// number of bytes in the array.
|
||||
//
|
||||
// It is an error to call this if the slot does not contain a string.
|
||||
const char* wrenGetSlotBytes(WrenVM* vm, int slot, int* length);
|
||||
WREN_API const char* wrenGetSlotBytes(WrenVM* vm, int slot, int* length);
|
||||
|
||||
// Reads a number from [slot].
|
||||
//
|
||||
// It is an error to call this if the slot does not contain a number.
|
||||
double wrenGetSlotDouble(WrenVM* vm, int slot);
|
||||
WREN_API double wrenGetSlotDouble(WrenVM* vm, int slot);
|
||||
|
||||
// Reads a foreign object from [slot] and returns a pointer to the foreign data
|
||||
// stored with it.
|
||||
//
|
||||
// It is an error to call this if the slot does not contain an instance of a
|
||||
// foreign class.
|
||||
void* wrenGetSlotForeign(WrenVM* vm, int slot);
|
||||
WREN_API void* wrenGetSlotForeign(WrenVM* vm, int slot);
|
||||
|
||||
// Reads a string from [slot].
|
||||
//
|
||||
@ -406,25 +437,25 @@ void* wrenGetSlotForeign(WrenVM* vm, int slot);
|
||||
// function returns, since the garbage collector may reclaim it.
|
||||
//
|
||||
// It is an error to call this if the slot does not contain a string.
|
||||
const char* wrenGetSlotString(WrenVM* vm, int slot);
|
||||
WREN_API const char* wrenGetSlotString(WrenVM* vm, int slot);
|
||||
|
||||
// Creates a handle for the value stored in [slot].
|
||||
//
|
||||
// This will prevent the object that is referred to from being garbage collected
|
||||
// until the handle is released by calling [wrenReleaseHandle()].
|
||||
WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
|
||||
WREN_API WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
|
||||
|
||||
// Stores the boolean [value] in [slot].
|
||||
void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
|
||||
WREN_API void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
|
||||
|
||||
// Stores the array [length] of [bytes] in [slot].
|
||||
//
|
||||
// The bytes are copied to a new string within Wren's heap, so you can free
|
||||
// memory used by them after this is called.
|
||||
void wrenSetSlotBytes(WrenVM* vm, int slot, const char* bytes, size_t length);
|
||||
WREN_API void wrenSetSlotBytes(WrenVM* vm, int slot, const char* bytes, size_t length);
|
||||
|
||||
// Stores the numeric [value] in [slot].
|
||||
void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
|
||||
WREN_API void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
|
||||
|
||||
// Creates a new instance of the foreign class stored in [classSlot] with [size]
|
||||
// bytes of raw storage and places the resulting object in [slot].
|
||||
@ -435,13 +466,16 @@ void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
|
||||
// and then the constructor will be invoked when the allocator returns.
|
||||
//
|
||||
// Returns a pointer to the foreign object's data.
|
||||
void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size);
|
||||
WREN_API void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size);
|
||||
|
||||
// Stores a new empty list in [slot].
|
||||
void wrenSetSlotNewList(WrenVM* vm, int slot);
|
||||
WREN_API void wrenSetSlotNewList(WrenVM* vm, int slot);
|
||||
|
||||
// Stores a new empty map in [slot].
|
||||
WREN_API void wrenSetSlotNewMap(WrenVM* vm, int slot);
|
||||
|
||||
// Stores null in [slot].
|
||||
void wrenSetSlotNull(WrenVM* vm, int slot);
|
||||
WREN_API void wrenSetSlotNull(WrenVM* vm, int slot);
|
||||
|
||||
// Stores the string [text] in [slot].
|
||||
//
|
||||
@ -449,40 +483,72 @@ void wrenSetSlotNull(WrenVM* vm, int slot);
|
||||
// memory used by it after this is called. The length is calculated using
|
||||
// [strlen()]. If the string may contain any null bytes in the middle, then you
|
||||
// should use [wrenSetSlotBytes()] instead.
|
||||
void wrenSetSlotString(WrenVM* vm, int slot, const char* text);
|
||||
WREN_API void wrenSetSlotString(WrenVM* vm, int slot, const char* text);
|
||||
|
||||
// Stores the value captured in [handle] in [slot].
|
||||
//
|
||||
// This does not release the handle for the value.
|
||||
void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
|
||||
WREN_API void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
|
||||
|
||||
// Returns the number of elements in the list stored in [slot].
|
||||
int wrenGetListCount(WrenVM* vm, int slot);
|
||||
WREN_API int wrenGetListCount(WrenVM* vm, int slot);
|
||||
|
||||
// Reads element [index] from the list in [listSlot] and stores it in
|
||||
// [elementSlot].
|
||||
void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);
|
||||
WREN_API void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);
|
||||
|
||||
// Sets the value stored at [index] in the list at [listSlot],
|
||||
// to the value from [elementSlot].
|
||||
WREN_API void wrenSetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);
|
||||
|
||||
// Takes the value stored at [elementSlot] and inserts it into the list stored
|
||||
// at [listSlot] at [index].
|
||||
//
|
||||
// As in Wren, negative indexes can be used to insert from the end. To append
|
||||
// an element, use `-1` for the index.
|
||||
void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot);
|
||||
WREN_API void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot);
|
||||
|
||||
// Returns the number of entries in the map stored in [slot].
|
||||
WREN_API int wrenGetMapCount(WrenVM* vm, int slot);
|
||||
|
||||
// Returns true if the key in [keySlot] is found in the map placed in [mapSlot].
|
||||
WREN_API bool wrenGetMapContainsKey(WrenVM* vm, int mapSlot, int keySlot);
|
||||
|
||||
// Retrieves a value with the key in [keySlot] from the map in [mapSlot] and
|
||||
// stores it in [valueSlot].
|
||||
WREN_API void wrenGetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot);
|
||||
|
||||
// Takes the value stored at [valueSlot] and inserts it into the map stored
|
||||
// at [mapSlot] with key [keySlot].
|
||||
WREN_API void wrenSetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot);
|
||||
|
||||
// Removes a value from the map in [mapSlot], with the key from [keySlot],
|
||||
// and place it in [removedValueSlot]. If not found, [removedValueSlot] is
|
||||
// set to null, the same behaviour as the Wren Map API.
|
||||
WREN_API void wrenRemoveMapValue(WrenVM* vm, int mapSlot, int keySlot,
|
||||
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,
|
||||
WREN_API void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
|
||||
int slot);
|
||||
|
||||
// Looks up the top level variable with [name] in resolved [module],
|
||||
// returns false if not found. The module must be imported at the time,
|
||||
// use wrenHasModule to ensure that before calling.
|
||||
WREN_API bool wrenHasVariable(WrenVM* vm, const char* module, const char* name);
|
||||
|
||||
// Returns true if [module] has been imported/resolved before, false if not.
|
||||
WREN_API bool wrenHasModule(WrenVM* vm, const char* module);
|
||||
|
||||
// Sets the current fiber to be aborted, and uses the value in [slot] as the
|
||||
// runtime error object.
|
||||
void wrenAbortFiber(WrenVM* vm, int slot);
|
||||
WREN_API void wrenAbortFiber(WrenVM* vm, int slot);
|
||||
|
||||
// Returns the user data associated with the WrenVM.
|
||||
void* wrenGetUserData(WrenVM* vm);
|
||||
WREN_API void* wrenGetUserData(WrenVM* vm);
|
||||
|
||||
// Sets user data associated with the WrenVM.
|
||||
void wrenSetUserData(WrenVM* vm, void* userData);
|
||||
WREN_API void wrenSetUserData(WrenVM* vm, void* userData);
|
||||
|
||||
#endif
|
||||
|
||||
@ -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 )
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@
|
||||
|
||||
#include "wren_common.h"
|
||||
#include "wren_core.h"
|
||||
#include "wren_math.h"
|
||||
#include "wren_primitive.h"
|
||||
#include "wren_value.h"
|
||||
|
||||
@ -49,6 +50,11 @@ DEF_PRIMITIVE(class_toString)
|
||||
RETURN_OBJ(AS_CLASS(args[0])->name);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(class_attributes)
|
||||
{
|
||||
RETURN_VAL(AS_CLASS(args[0])->attributes);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fiber_new)
|
||||
{
|
||||
if (!validateFn(vm, args[1], "Argument")) return false;
|
||||
@ -191,6 +197,15 @@ DEF_PRIMITIVE(fiber_try)
|
||||
return false;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fiber_try1)
|
||||
{
|
||||
runFiber(vm, AS_FIBER(args[0]), args, true, true, "try");
|
||||
|
||||
// If we're switching to a valid fiber to try, remember that we're trying it.
|
||||
if (!wrenHasError(vm->fiber)) vm->fiber->state = FIBER_TRY;
|
||||
return false;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fiber_yield)
|
||||
{
|
||||
ObjFiber* current = vm->fiber;
|
||||
@ -248,23 +263,16 @@ DEF_PRIMITIVE(fn_arity)
|
||||
|
||||
static void call_fn(WrenVM* vm, Value* args, int numArgs)
|
||||
{
|
||||
// We only care about missing arguments, not extras.
|
||||
if (AS_CLOSURE(args[0])->fn->arity > numArgs)
|
||||
{
|
||||
vm->fiber->error = CONST_STRING(vm, "Function expects more arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
// +1 to include the function itself.
|
||||
wrenCallFunction(vm, vm->fiber, AS_CLOSURE(args[0]), numArgs + 1);
|
||||
}
|
||||
|
||||
#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)
|
||||
@ -391,6 +399,34 @@ DEF_PRIMITIVE(list_removeAt)
|
||||
RETURN_VAL(wrenListRemoveAt(vm, list, index));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(list_removeValue) {
|
||||
ObjList* list = AS_LIST(args[0]);
|
||||
int index = wrenListIndexOf(vm, list, args[1]);
|
||||
if(index == -1) RETURN_NULL;
|
||||
RETURN_VAL(wrenListRemoveAt(vm, list, index));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(list_indexOf)
|
||||
{
|
||||
ObjList* list = AS_LIST(args[0]);
|
||||
RETURN_NUM(wrenListIndexOf(vm, list, args[1]));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(list_swap)
|
||||
{
|
||||
ObjList* list = AS_LIST(args[0]);
|
||||
uint32_t indexA = validateIndex(vm, args[1], list->elements.count, "Index 0");
|
||||
if (indexA == UINT32_MAX) return false;
|
||||
uint32_t indexB = validateIndex(vm, args[2], list->elements.count, "Index 1");
|
||||
if (indexB == UINT32_MAX) return false;
|
||||
|
||||
Value a = list->elements.data[indexA];
|
||||
list->elements.data[indexA] = list->elements.data[indexB];
|
||||
list->elements.data[indexB] = a;
|
||||
|
||||
RETURN_NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(list_subscript)
|
||||
{
|
||||
ObjList* list = AS_LIST(args[0]);
|
||||
@ -594,17 +630,30 @@ DEF_PRIMITIVE(num_fromString)
|
||||
RETURN_NUM(number);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_pi)
|
||||
{
|
||||
RETURN_NUM(3.14159265358979323846);
|
||||
}
|
||||
// Defines a primitive on Num that calls infix [op] and returns [type].
|
||||
#define DEF_NUM_CONSTANT(name, value) \
|
||||
DEF_PRIMITIVE(num_##name) \
|
||||
{ \
|
||||
RETURN_NUM(value); \
|
||||
}
|
||||
|
||||
DEF_NUM_CONSTANT(infinity, INFINITY)
|
||||
DEF_NUM_CONSTANT(nan, WREN_DOUBLE_NAN)
|
||||
DEF_NUM_CONSTANT(pi, 3.14159265358979323846264338327950288)
|
||||
DEF_NUM_CONSTANT(tau, 6.28318530717958647692528676655900577)
|
||||
|
||||
DEF_NUM_CONSTANT(largest, DBL_MAX)
|
||||
DEF_NUM_CONSTANT(smallest, DBL_MIN)
|
||||
|
||||
DEF_NUM_CONSTANT(maxSafeInteger, 9007199254740991.0)
|
||||
DEF_NUM_CONSTANT(minSafeInteger, -9007199254740991.0)
|
||||
|
||||
// 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 +666,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,16 +682,17 @@ 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)
|
||||
DEF_NUM_FN(acos, acos)
|
||||
DEF_NUM_FN(asin, asin)
|
||||
DEF_NUM_FN(atan, atan)
|
||||
DEF_NUM_FN(cbrt, cbrt)
|
||||
DEF_NUM_FN(ceil, ceil)
|
||||
DEF_NUM_FN(cos, cos)
|
||||
DEF_NUM_FN(floor, floor)
|
||||
@ -652,6 +702,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)
|
||||
{
|
||||
@ -697,18 +749,52 @@ DEF_PRIMITIVE(num_dotDotDot)
|
||||
|
||||
DEF_PRIMITIVE(num_atan2)
|
||||
{
|
||||
if (!validateNum(vm, args[1], "x value")) return false;
|
||||
|
||||
RETURN_NUM(atan2(AS_NUM(args[0]), AS_NUM(args[1])));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_min)
|
||||
{
|
||||
if (!validateNum(vm, args[1], "Other value")) return false;
|
||||
|
||||
double value = AS_NUM(args[0]);
|
||||
double other = AS_NUM(args[1]);
|
||||
RETURN_NUM(value <= other ? value : other);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_max)
|
||||
{
|
||||
if (!validateNum(vm, args[1], "Other value")) return false;
|
||||
|
||||
double value = AS_NUM(args[0]);
|
||||
double other = AS_NUM(args[1]);
|
||||
RETURN_NUM(value > other ? value : other);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_clamp)
|
||||
{
|
||||
if (!validateNum(vm, args[1], "Min value")) return false;
|
||||
if (!validateNum(vm, args[2], "Max value")) return false;
|
||||
|
||||
double value = AS_NUM(args[0]);
|
||||
double min = AS_NUM(args[1]);
|
||||
double max = AS_NUM(args[2]);
|
||||
double result = (value < min) ? min : ((value > max) ? max : value);
|
||||
RETURN_NUM(result);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_pow)
|
||||
{
|
||||
if (!validateNum(vm, args[1], "Power value")) return false;
|
||||
|
||||
RETURN_NUM(pow(AS_NUM(args[0]), AS_NUM(args[1])));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_fraction)
|
||||
{
|
||||
double dummy;
|
||||
RETURN_NUM(modf(AS_NUM(args[0]) , &dummy));
|
||||
double unused;
|
||||
RETURN_NUM(modf(AS_NUM(args[0]) , &unused));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_isInfinity)
|
||||
@ -745,16 +831,6 @@ DEF_PRIMITIVE(num_sign)
|
||||
}
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_largest)
|
||||
{
|
||||
RETURN_NUM(DBL_MAX);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_smallest)
|
||||
{
|
||||
RETURN_NUM(DBL_MIN);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_toString)
|
||||
{
|
||||
RETURN_VAL(wrenNumToString(vm, AS_NUM(args[0])));
|
||||
@ -1181,6 +1257,7 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->classClass, "name", class_name);
|
||||
PRIMITIVE(vm->classClass, "supertype", class_supertype);
|
||||
PRIMITIVE(vm->classClass, "toString", class_toString);
|
||||
PRIMITIVE(vm->classClass, "attributes", class_attributes);
|
||||
|
||||
// Finally, we can define Object's metaclass which is a subclass of Class.
|
||||
ObjClass* objectMetaclass = defineClass(vm, coreModule, "Object metaclass");
|
||||
@ -1240,28 +1317,31 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->fiberClass, "transfer(_)", fiber_transfer1);
|
||||
PRIMITIVE(vm->fiberClass, "transferError(_)", fiber_transferError);
|
||||
PRIMITIVE(vm->fiberClass, "try()", fiber_try);
|
||||
PRIMITIVE(vm->fiberClass, "try(_)", fiber_try1);
|
||||
|
||||
vm->fnClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Fn"));
|
||||
PRIMITIVE(vm->fnClass->obj.classObj, "new(_)", fn_new);
|
||||
|
||||
PRIMITIVE(vm->fnClass, "arity", fn_arity);
|
||||
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"));
|
||||
@ -1270,9 +1350,14 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
|
||||
vm->numClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Num"));
|
||||
PRIMITIVE(vm->numClass->obj.classObj, "fromString(_)", num_fromString);
|
||||
PRIMITIVE(vm->numClass->obj.classObj, "infinity", num_infinity);
|
||||
PRIMITIVE(vm->numClass->obj.classObj, "nan", num_nan);
|
||||
PRIMITIVE(vm->numClass->obj.classObj, "pi", num_pi);
|
||||
PRIMITIVE(vm->numClass->obj.classObj, "tau", num_tau);
|
||||
PRIMITIVE(vm->numClass->obj.classObj, "largest", num_largest);
|
||||
PRIMITIVE(vm->numClass->obj.classObj, "smallest", num_smallest);
|
||||
PRIMITIVE(vm->numClass->obj.classObj, "maxSafeInteger", num_maxSafeInteger);
|
||||
PRIMITIVE(vm->numClass->obj.classObj, "minSafeInteger", num_minSafeInteger);
|
||||
PRIMITIVE(vm->numClass, "-(_)", num_minus);
|
||||
PRIMITIVE(vm->numClass, "+(_)", num_plus);
|
||||
PRIMITIVE(vm->numClass, "*(_)", num_multiply);
|
||||
@ -1290,15 +1375,21 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->numClass, "acos", num_acos);
|
||||
PRIMITIVE(vm->numClass, "asin", num_asin);
|
||||
PRIMITIVE(vm->numClass, "atan", num_atan);
|
||||
PRIMITIVE(vm->numClass, "cbrt", num_cbrt);
|
||||
PRIMITIVE(vm->numClass, "ceil", num_ceil);
|
||||
PRIMITIVE(vm->numClass, "cos", num_cos);
|
||||
PRIMITIVE(vm->numClass, "floor", num_floor);
|
||||
PRIMITIVE(vm->numClass, "-", num_negate);
|
||||
PRIMITIVE(vm->numClass, "round", num_round);
|
||||
PRIMITIVE(vm->numClass, "min(_)", num_min);
|
||||
PRIMITIVE(vm->numClass, "max(_)", num_max);
|
||||
PRIMITIVE(vm->numClass, "clamp(_,_)", num_clamp);
|
||||
PRIMITIVE(vm->numClass, "sin", num_sin);
|
||||
PRIMITIVE(vm->numClass, "sqrt", num_sqrt);
|
||||
PRIMITIVE(vm->numClass, "tan", num_tan);
|
||||
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);
|
||||
@ -1349,6 +1440,9 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->listClass, "iterate(_)", list_iterate);
|
||||
PRIMITIVE(vm->listClass, "iteratorValue(_)", list_iteratorValue);
|
||||
PRIMITIVE(vm->listClass, "removeAt(_)", list_removeAt);
|
||||
PRIMITIVE(vm->listClass, "remove(_)", list_removeValue);
|
||||
PRIMITIVE(vm->listClass, "indexOf(_)", list_indexOf);
|
||||
PRIMITIVE(vm->listClass, "swap(_,_)", list_swap);
|
||||
|
||||
vm->mapClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Map"));
|
||||
PRIMITIVE(vm->mapClass->obj.classObj, "new()", map_new);
|
||||
|
||||
@ -323,6 +323,41 @@ class List is Sequence {
|
||||
return other
|
||||
}
|
||||
|
||||
sort() { sort {|low, high| low < high } }
|
||||
|
||||
sort(comparer) {
|
||||
if (!(comparer is Fn)) {
|
||||
Fiber.abort("Comparer must be a function.")
|
||||
}
|
||||
quicksort_(0, count - 1, comparer)
|
||||
return this
|
||||
}
|
||||
|
||||
quicksort_(low, high, comparer) {
|
||||
if (low < high) {
|
||||
var p = partition_(low, high, comparer)
|
||||
quicksort_(low, p - 1, comparer)
|
||||
quicksort_(p + 1, high, comparer)
|
||||
}
|
||||
}
|
||||
|
||||
partition_(low, high, comparer) {
|
||||
var p = this[high]
|
||||
var i = low - 1
|
||||
for (j in low..(high-1)) {
|
||||
if (comparer.call(this[j], p)) {
|
||||
i = i + 1
|
||||
var t = this[i]
|
||||
this[i] = this[j]
|
||||
this[j] = t
|
||||
}
|
||||
}
|
||||
var t = this[i+1]
|
||||
this[i+1] = this[high]
|
||||
this[high] = t
|
||||
return i+1
|
||||
}
|
||||
|
||||
toString { "[%(join(", "))]" }
|
||||
|
||||
+(other) {
|
||||
@ -436,3 +471,13 @@ class System {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ClassAttributes {
|
||||
self { _attributes }
|
||||
methods { _methods }
|
||||
construct new(attributes, methods) {
|
||||
_attributes = attributes
|
||||
_methods = methods
|
||||
}
|
||||
toString { "attributes:%(_attributes) methods:%(_methods)" }
|
||||
}
|
||||
@ -325,6 +325,41 @@ static const char* coreModuleSource =
|
||||
" return other\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" sort() { sort {|low, high| low < high } }\n"
|
||||
"\n"
|
||||
" sort(comparer) {\n"
|
||||
" if (!(comparer is Fn)) {\n"
|
||||
" Fiber.abort(\"Comparer must be a function.\")\n"
|
||||
" }\n"
|
||||
" quicksort_(0, count - 1, comparer)\n"
|
||||
" return this\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" quicksort_(low, high, comparer) {\n"
|
||||
" if (low < high) {\n"
|
||||
" var p = partition_(low, high, comparer)\n"
|
||||
" quicksort_(low, p - 1, comparer)\n"
|
||||
" quicksort_(p + 1, high, comparer)\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" partition_(low, high, comparer) {\n"
|
||||
" var p = this[high]\n"
|
||||
" var i = low - 1\n"
|
||||
" for (j in low..(high-1)) {\n"
|
||||
" if (comparer.call(this[j], p)) { \n"
|
||||
" i = i + 1\n"
|
||||
" var t = this[i]\n"
|
||||
" this[i] = this[j]\n"
|
||||
" this[j] = t\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" var t = this[i+1]\n"
|
||||
" this[i+1] = this[high]\n"
|
||||
" this[high] = t\n"
|
||||
" return i+1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" toString { \"[%(join(\", \"))]\" }\n"
|
||||
"\n"
|
||||
" +(other) {\n"
|
||||
@ -437,4 +472,15 @@ static const char* coreModuleSource =
|
||||
" writeString_(\"[invalid toString]\")\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class ClassAttributes {\n"
|
||||
" self { _attributes }\n"
|
||||
" methods { _methods }\n"
|
||||
" construct new(attributes, methods) {\n"
|
||||
" _attributes = attributes\n"
|
||||
" _methods = methods\n"
|
||||
" }\n"
|
||||
" toString { \"attributes:%(_attributes) methods:%(_methods)\" }\n"
|
||||
"}\n";
|
||||
|
||||
|
||||
@ -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)
|
||||
{
|
||||
@ -296,6 +296,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
}
|
||||
|
||||
case CODE_FOREIGN_CLASS: printf("FOREIGN_CLASS\n"); break;
|
||||
case CODE_END_CLASS: printf("END_CLASS\n"); break;
|
||||
|
||||
case CODE_METHOD_INSTANCE:
|
||||
{
|
||||
|
||||
34
src/vm/wren_math.h
Normal file
34
src/vm/wren_math.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef wren_math_h
|
||||
#define wren_math_h
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// A union to let us reinterpret a double as raw bits and back.
|
||||
typedef union
|
||||
{
|
||||
uint64_t bits64;
|
||||
uint32_t bits32[2];
|
||||
double num;
|
||||
} WrenDoubleBits;
|
||||
|
||||
#define WREN_DOUBLE_QNAN_POS_MIN_BITS (UINT64_C(0x7FF8000000000000))
|
||||
#define WREN_DOUBLE_QNAN_POS_MAX_BITS (UINT64_C(0x7FFFFFFFFFFFFFFF))
|
||||
|
||||
#define WREN_DOUBLE_NAN (wrenDoubleFromBits(WREN_DOUBLE_QNAN_POS_MIN_BITS))
|
||||
|
||||
static inline double wrenDoubleFromBits(uint64_t bits)
|
||||
{
|
||||
WrenDoubleBits data;
|
||||
data.bits64 = bits;
|
||||
return data.num;
|
||||
}
|
||||
|
||||
static inline uint64_t wrenDoubleToBits(double num)
|
||||
{
|
||||
WrenDoubleBits data;
|
||||
data.num = num;
|
||||
return data.bits64;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -169,6 +169,10 @@ OPCODE(FOREIGN_CONSTRUCT, 0)
|
||||
// the name of the class. Byte [arg] is the number of fields in the class.
|
||||
OPCODE(CLASS, -1)
|
||||
|
||||
// Ends a class.
|
||||
// Atm the stack contains the class and the ClassAttributes (or null).
|
||||
OPCODE(END_CLASS, -2)
|
||||
|
||||
// Creates a foreign class. Top of stack is the superclass. Below that is a
|
||||
// string for the name of the class.
|
||||
OPCODE(FOREIGN_CLASS, -1)
|
||||
|
||||
@ -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)
|
||||
@ -49,11 +47,7 @@ bool validateInt(WrenVM* vm, Value arg, const char* argName)
|
||||
|
||||
bool validateKey(WrenVM* vm, Value arg)
|
||||
{
|
||||
if (IS_BOOL(arg) || IS_CLASS(arg) || IS_NULL(arg) ||
|
||||
IS_NUM(arg) || IS_RANGE(arg) || IS_STRING(arg))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (wrenMapIsValidKey(arg)) return true;
|
||||
|
||||
RETURN_ERROR("Key must be a value type.");
|
||||
}
|
||||
|
||||
@ -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.
|
||||
@ -60,7 +81,7 @@ bool validateInt(WrenVM* vm, Value arg, const char* argName);
|
||||
|
||||
// Validates that [arg] is a valid object for use as a map key. Returns true if
|
||||
// it is. If not, reports an error and returns false.
|
||||
bool validateKey(WrenVM* vm, Value arg);
|
||||
bool validateKey(WrenVM * vm, Value arg);
|
||||
|
||||
// Validates that the argument at [argIndex] is an integer within `[0, count)`.
|
||||
// Also allows negative indices which map backwards from the end. Returns the
|
||||
|
||||
@ -194,3 +194,14 @@ int wrenPowerOf2Ceil(int n)
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
uint32_t wrenValidateIndex(uint32_t count, int64_t value)
|
||||
{
|
||||
// Negative indices count from the end.
|
||||
if (value < 0) value = count + value;
|
||||
|
||||
// Check bounds.
|
||||
if (value >= 0 && value < count) return (uint32_t)value;
|
||||
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -118,4 +118,9 @@ int wrenUtf8DecodeNumBytes(uint8_t byte);
|
||||
// Returns the smallest power of two that is equal to or greater than [n].
|
||||
int wrenPowerOf2Ceil(int n);
|
||||
|
||||
// Validates that [value] is within `[0, count)`. Also allows
|
||||
// negative indices which map backwards from the end. Returns the valid positive
|
||||
// index value. If invalid, returns `UINT32_MAX`.
|
||||
uint32_t wrenValidateIndex(uint32_t count, int64_t value);
|
||||
|
||||
#endif
|
||||
|
||||
@ -50,6 +50,7 @@ ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name)
|
||||
classObj->superclass = NULL;
|
||||
classObj->numFields = numFields;
|
||||
classObj->name = name;
|
||||
classObj->attributes = NULL_VAL;
|
||||
|
||||
wrenPushRoot(vm, (Obj*)classObj);
|
||||
wrenMethodBufferInit(&classObj->methods);
|
||||
@ -319,6 +320,19 @@ void wrenListInsert(WrenVM* vm, ObjList* list, Value value, uint32_t index)
|
||||
list->elements.data[index] = value;
|
||||
}
|
||||
|
||||
int wrenListIndexOf(WrenVM* vm, ObjList* list, Value value)
|
||||
{
|
||||
int count = list->elements.count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Value item = list->elements.data[i];
|
||||
if(wrenValuesEqual(item, value)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Value wrenListRemoveAt(WrenVM* vm, ObjList* list, uint32_t index)
|
||||
{
|
||||
Value removed = list->elements.data[index];
|
||||
@ -374,9 +388,7 @@ static inline uint32_t hashBits(uint64_t hash)
|
||||
static inline uint32_t hashNumber(double num)
|
||||
{
|
||||
// Hash the raw bits of the value.
|
||||
DoubleBits bits;
|
||||
bits.num = num;
|
||||
return hashBits(bits.bits64);
|
||||
return hashBits(wrenDoubleToBits(num));
|
||||
}
|
||||
|
||||
// Generates a hash code for [object].
|
||||
@ -977,7 +989,8 @@ void wrenGrayObj(WrenVM* vm, Obj* obj)
|
||||
{
|
||||
vm->grayCapacity = vm->grayCount * 2;
|
||||
vm->gray = (Obj**)vm->config.reallocateFn(vm->gray,
|
||||
vm->grayCapacity * sizeof(Obj*));
|
||||
vm->grayCapacity * sizeof(Obj*),
|
||||
vm->config.userData);
|
||||
}
|
||||
|
||||
vm->gray[vm->grayCount++] = obj;
|
||||
@ -1016,6 +1029,8 @@ static void blackenClass(WrenVM* vm, ObjClass* classObj)
|
||||
|
||||
wrenGrayObj(vm, (Obj*)classObj->name);
|
||||
|
||||
if(!IS_NULL(classObj->attributes)) wrenGrayObj(vm, AS_OBJ(classObj->attributes));
|
||||
|
||||
// Keep track of how much memory is still in use.
|
||||
vm->bytesAllocated += sizeof(ObjClass);
|
||||
vm->bytesAllocated += classObj->methods.capacity * sizeof(Method);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "wren_common.h"
|
||||
#include "wren_math.h"
|
||||
#include "wren_utils.h"
|
||||
|
||||
// This defines the built-in types and their core representations in memory.
|
||||
@ -76,6 +77,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 +184,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;
|
||||
@ -194,7 +196,7 @@ typedef struct sObjUpvalue
|
||||
|
||||
// The type of a primitive function.
|
||||
//
|
||||
// Primitives are similiar to foreign functions, but have more direct access to
|
||||
// Primitives are similar to foreign functions, but have more direct access to
|
||||
// VM internals. It is passed the arguments in [args]. If it returns a value,
|
||||
// it places it in `args[0]` and returns `true`. If it causes a runtime error
|
||||
// or modifies the running fiber, it returns `false`.
|
||||
@ -316,8 +318,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 +360,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,
|
||||
|
||||
@ -405,6 +410,9 @@ struct sObjClass
|
||||
|
||||
// The name of the class.
|
||||
ObjString* name;
|
||||
|
||||
// The ClassAttribute for the class, if any
|
||||
Value attributes;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
@ -609,14 +617,6 @@ typedef struct
|
||||
|
||||
#endif
|
||||
|
||||
// A union to let us reinterpret a double as raw bits and back.
|
||||
typedef union
|
||||
{
|
||||
uint64_t bits64;
|
||||
uint32_t bits32[2];
|
||||
double num;
|
||||
} DoubleBits;
|
||||
|
||||
// Creates a new "raw" class. It has no metaclass or superclass whatsoever.
|
||||
// This is only used for bootstrapping the initial Object and Class classes,
|
||||
// which are a little special.
|
||||
@ -683,9 +683,17 @@ void wrenListInsert(WrenVM* vm, ObjList* list, Value value, uint32_t index);
|
||||
// Removes and returns the item at [index] from [list].
|
||||
Value wrenListRemoveAt(WrenVM* vm, ObjList* list, uint32_t index);
|
||||
|
||||
// Searches for [value] in [list], returns the index or -1 if not found.
|
||||
int wrenListIndexOf(WrenVM* vm, ObjList* list, Value value);
|
||||
|
||||
// Creates a new empty map.
|
||||
ObjMap* wrenNewMap(WrenVM* vm);
|
||||
|
||||
// Validates that [arg] is a valid object for use as a map key. Returns true if
|
||||
// it is and returns false otherwise. Use validateKey usually, for a runtime error.
|
||||
// This separation exists to aid the API in surfacing errors to the developer as well.
|
||||
static inline bool wrenMapIsValidKey(Value arg);
|
||||
|
||||
// Looks up [key] in [map]. If found, returns the value. Otherwise, returns
|
||||
// `UNDEFINED_VAL`.
|
||||
Value wrenMapGet(ObjMap* map, Value key);
|
||||
@ -850,9 +858,7 @@ static inline Value wrenObjectToValue(Obj* obj)
|
||||
static inline double wrenValueToNum(Value value)
|
||||
{
|
||||
#if WREN_NAN_TAGGING
|
||||
DoubleBits data;
|
||||
data.bits64 = value;
|
||||
return data.num;
|
||||
return wrenDoubleFromBits(value);
|
||||
#else
|
||||
return value.as.num;
|
||||
#endif
|
||||
@ -862,9 +868,7 @@ static inline double wrenValueToNum(Value value)
|
||||
static inline Value wrenNumToValue(double num)
|
||||
{
|
||||
#if WREN_NAN_TAGGING
|
||||
DoubleBits data;
|
||||
data.num = num;
|
||||
return data.bits64;
|
||||
return wrenDoubleToBits(num);
|
||||
#else
|
||||
Value value;
|
||||
value.type = VAL_NUM;
|
||||
@ -873,4 +877,14 @@ static inline Value wrenNumToValue(double num)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool wrenMapIsValidKey(Value arg)
|
||||
{
|
||||
return IS_BOOL(arg)
|
||||
|| IS_CLASS(arg)
|
||||
|| IS_NULL(arg)
|
||||
|| IS_NUM(arg)
|
||||
|| IS_RANGE(arg)
|
||||
|| IS_STRING(arg);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
342
src/vm/wren_vm.c
342
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
|
||||
@ -24,7 +25,7 @@
|
||||
// may return a non-NULL pointer which must not be dereferenced but nevertheless
|
||||
// should be freed. To prevent that, we avoid calling realloc() with a zero
|
||||
// size.
|
||||
static void* defaultReallocate(void* ptr, size_t newSize)
|
||||
static void* defaultReallocate(void* ptr, size_t newSize, void* _)
|
||||
{
|
||||
if (newSize == 0)
|
||||
{
|
||||
@ -35,6 +36,11 @@ static void* defaultReallocate(void* ptr, size_t newSize)
|
||||
return realloc(ptr, newSize);
|
||||
}
|
||||
|
||||
int wrenGetVersionNumber()
|
||||
{
|
||||
return WREN_VERSION_NUMBER;
|
||||
}
|
||||
|
||||
void wrenInitConfiguration(WrenConfiguration* config)
|
||||
{
|
||||
config->reallocateFn = defaultReallocate;
|
||||
@ -53,15 +59,23 @@ void wrenInitConfiguration(WrenConfiguration* config)
|
||||
WrenVM* wrenNewVM(WrenConfiguration* config)
|
||||
{
|
||||
WrenReallocateFn reallocate = defaultReallocate;
|
||||
if (config != NULL) reallocate = config->reallocateFn;
|
||||
void* userData = NULL;
|
||||
if (config != NULL) {
|
||||
userData = config->userData;
|
||||
reallocate = config->reallocateFn ? config->reallocateFn : defaultReallocate;
|
||||
}
|
||||
|
||||
WrenVM* vm = (WrenVM*)reallocate(NULL, sizeof(*vm));
|
||||
WrenVM* vm = (WrenVM*)reallocate(NULL, sizeof(*vm), userData);
|
||||
memset(vm, 0, sizeof(WrenVM));
|
||||
|
||||
// Copy the configuration if given one.
|
||||
if (config != NULL)
|
||||
{
|
||||
memcpy(&vm->config, config, sizeof(WrenConfiguration));
|
||||
|
||||
// We choose to set this after copying,
|
||||
// rather than modifying the user config pointer
|
||||
vm->config.reallocateFn = reallocate;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -72,7 +86,7 @@ WrenVM* wrenNewVM(WrenConfiguration* config)
|
||||
vm->grayCount = 0;
|
||||
// TODO: Tune this.
|
||||
vm->grayCapacity = 4;
|
||||
vm->gray = (Obj**)reallocate(NULL, vm->grayCapacity * sizeof(Obj*));
|
||||
vm->gray = (Obj**)reallocate(NULL, vm->grayCapacity * sizeof(Obj*), userData);
|
||||
vm->nextGC = vm->config.initialHeapSize;
|
||||
|
||||
wrenSymbolTableInit(&vm->methodNames);
|
||||
@ -96,7 +110,7 @@ void wrenFreeVM(WrenVM* vm)
|
||||
}
|
||||
|
||||
// Free up the GC gray set.
|
||||
vm->gray = (Obj**)vm->config.reallocateFn(vm->gray, 0);
|
||||
vm->gray = (Obj**)vm->config.reallocateFn(vm->gray, 0, vm->config.userData);
|
||||
|
||||
// Tell the user if they didn't free any handles. We don't want to just free
|
||||
// them here because the host app may still have pointers to them that they
|
||||
@ -187,12 +201,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
|
||||
}
|
||||
|
||||
@ -219,7 +233,7 @@ void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize)
|
||||
if (newSize > 0 && vm->bytesAllocated > vm->nextGC) wrenCollectGarbage(vm);
|
||||
#endif
|
||||
|
||||
return vm->config.reallocateFn(memory, newSize);
|
||||
return vm->config.reallocateFn(memory, newSize, vm->config.userData);
|
||||
}
|
||||
|
||||
// Captures the local variable [local] into an [Upvalue]. If that local is
|
||||
@ -445,10 +459,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++)
|
||||
@ -504,7 +525,10 @@ static Value validateSuperclass(WrenVM* vm, Value name, Value superclassValue,
|
||||
superclass == vm->listClass ||
|
||||
superclass == vm->mapClass ||
|
||||
superclass == vm->rangeClass ||
|
||||
superclass == vm->stringClass)
|
||||
superclass == vm->stringClass ||
|
||||
superclass == vm->boolClass ||
|
||||
superclass == vm->nullClass ||
|
||||
superclass == vm->numClass)
|
||||
{
|
||||
return wrenStringFormat(vm,
|
||||
"Class '@' cannot inherit from built-in class '@'.",
|
||||
@ -583,6 +607,27 @@ static void bindForeignClass(WrenVM* vm, ObjClass* classObj, ObjModule* module)
|
||||
}
|
||||
}
|
||||
|
||||
// Completes the process for creating a new class.
|
||||
//
|
||||
// The class attributes instance and the class itself should be on the
|
||||
// top of the fiber's stack.
|
||||
//
|
||||
// This process handles moving the attribute data for a class from
|
||||
// compile time to runtime, since it now has all the attributes associated
|
||||
// with a class, including for methods.
|
||||
static void endClass(WrenVM* vm)
|
||||
{
|
||||
// Pull the attributes and class off the stack
|
||||
Value attributes = vm->fiber->stackTop[-2];
|
||||
Value classValue = vm->fiber->stackTop[-1];
|
||||
|
||||
// Remove the stack items
|
||||
vm->fiber->stackTop -= 2;
|
||||
|
||||
ObjClass* classObj = AS_CLASS(classValue);
|
||||
classObj->attributes = attributes;
|
||||
}
|
||||
|
||||
// Creates a new class.
|
||||
//
|
||||
// If [numFields] is -1, the class is a foreign class. The name and superclass
|
||||
@ -693,44 +738,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)
|
||||
{
|
||||
@ -766,6 +806,21 @@ static Value getModuleVariable(WrenVM* vm, ObjModule* module,
|
||||
return NULL_VAL;
|
||||
}
|
||||
|
||||
inline static bool checkArity(WrenVM* vm, Value value, int numArgs)
|
||||
{
|
||||
ASSERT(IS_CLOSURE(value), "Receiver must be a closure.");
|
||||
ObjFn* fn = AS_CLOSURE(value)->fn;
|
||||
|
||||
// We only care about missing arguments, not extras. The "- 1" is because
|
||||
// numArgs includes the receiver, the function itself, which we don't want to
|
||||
// count.
|
||||
if (numArgs - 1 >= fn->arity) return true;
|
||||
|
||||
vm->fiber->error = CONST_STRING(vm, "Function expects more arguments.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// The main bytecode interpreter loop. This is where the magic happens. It is
|
||||
// also, as you can imagine, highly performance critical.
|
||||
static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
@ -797,36 +852,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 +898,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 +1062,17 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
}
|
||||
break;
|
||||
|
||||
case METHOD_FUNCTION_CALL:
|
||||
if (!checkArity(vm, args[0], numArgs)) {
|
||||
RUNTIME_ERROR();
|
||||
break;
|
||||
}
|
||||
|
||||
STORE_FRAME();
|
||||
method->as.primitive(vm, args);
|
||||
LOAD_FRAME();
|
||||
break;
|
||||
|
||||
case METHOD_FOREIGN:
|
||||
callForeign(vm, fiber, method->as.foreign, numArgs);
|
||||
if (wrenHasError(fiber)) RUNTIME_ERROR();
|
||||
@ -1100,7 +1166,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
uint16_t offset = READ_SHORT();
|
||||
Value condition = POP();
|
||||
|
||||
if (IS_FALSE(condition) || IS_NULL(condition)) ip += offset;
|
||||
if (wrenIsFalsyValue(condition)) ip += offset;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -1109,7 +1175,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
uint16_t offset = READ_SHORT();
|
||||
Value condition = PEEK();
|
||||
|
||||
if (IS_FALSE(condition) || IS_NULL(condition))
|
||||
if (wrenIsFalsyValue(condition))
|
||||
{
|
||||
// Short-circuit the right hand side.
|
||||
ip += offset;
|
||||
@ -1127,7 +1193,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
uint16_t offset = READ_SHORT();
|
||||
Value condition = PEEK();
|
||||
|
||||
if (IS_FALSE(condition) || IS_NULL(condition))
|
||||
if (wrenIsFalsyValue(condition))
|
||||
{
|
||||
// Discard the condition and evaluate the right hand side.
|
||||
DROP();
|
||||
@ -1229,6 +1295,13 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
CASE_CODE(END_CLASS):
|
||||
{
|
||||
endClass(vm);
|
||||
if (wrenHasError(fiber)) RUNTIME_ERROR();
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
CASE_CODE(CLASS):
|
||||
{
|
||||
createClass(vm, READ_BYTE(), NULL);
|
||||
@ -1446,7 +1519,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 +1666,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 +1765,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);
|
||||
@ -1723,9 +1803,27 @@ void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot)
|
||||
validateApiSlot(vm, listSlot);
|
||||
validateApiSlot(vm, elementSlot);
|
||||
ASSERT(IS_LIST(vm->apiStack[listSlot]), "Slot must hold a list.");
|
||||
|
||||
|
||||
ValueBuffer elements = AS_LIST(vm->apiStack[listSlot])->elements;
|
||||
vm->apiStack[elementSlot] = elements.data[index];
|
||||
|
||||
uint32_t usedIndex = wrenValidateIndex(elements.count, index);
|
||||
ASSERT(usedIndex != UINT32_MAX, "Index out of bounds.");
|
||||
|
||||
vm->apiStack[elementSlot] = elements.data[usedIndex];
|
||||
}
|
||||
|
||||
void wrenSetListElement(WrenVM* vm, int listSlot, int index, int elementSlot)
|
||||
{
|
||||
validateApiSlot(vm, listSlot);
|
||||
validateApiSlot(vm, elementSlot);
|
||||
ASSERT(IS_LIST(vm->apiStack[listSlot]), "Slot must hold a list.");
|
||||
|
||||
ObjList* list = AS_LIST(vm->apiStack[listSlot]);
|
||||
|
||||
uint32_t usedIndex = wrenValidateIndex(list->elements.count, index);
|
||||
ASSERT(usedIndex != UINT32_MAX, "Index out of bounds.");
|
||||
|
||||
list->elements.data[usedIndex] = vm->apiStack[elementSlot];
|
||||
}
|
||||
|
||||
void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
|
||||
@ -1736,7 +1834,8 @@ void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
|
||||
|
||||
ObjList* list = AS_LIST(vm->apiStack[listSlot]);
|
||||
|
||||
// Negative indices count from the end.
|
||||
// Negative indices count from the end.
|
||||
// We don't use wrenValidateIndex here because insert allows 1 past the end.
|
||||
if (index < 0) index = list->elements.count + 1 + index;
|
||||
|
||||
ASSERT(index <= list->elements.count, "Index out of bounds.");
|
||||
@ -1744,13 +1843,90 @@ 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];
|
||||
ASSERT(wrenMapIsValidKey(key), "Key must be a value type");
|
||||
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];
|
||||
ASSERT(wrenMapIsValidKey(key), "Key must be a value type");
|
||||
|
||||
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));
|
||||
|
||||
@ -1766,6 +1942,40 @@ void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
|
||||
setSlot(vm, slot, moduleObj->variables.data[variableSlot]);
|
||||
}
|
||||
|
||||
bool wrenHasVariable(WrenVM* vm, const char* module, const char* name)
|
||||
{
|
||||
ASSERT(module != NULL, "Module cannot be NULL.");
|
||||
ASSERT(name != NULL, "Variable name cannot be NULL.");
|
||||
|
||||
Value moduleName = wrenStringFormat(vm, "$", module);
|
||||
wrenPushRoot(vm, AS_OBJ(moduleName));
|
||||
|
||||
//We don't use wrenHasModule since we want to use the module object.
|
||||
ObjModule* moduleObj = getModule(vm, moduleName);
|
||||
ASSERT(moduleObj != NULL, "Could not find module.");
|
||||
|
||||
wrenPopRoot(vm); // moduleName.
|
||||
|
||||
int variableSlot = wrenSymbolTableFind(&moduleObj->variableNames,
|
||||
name, strlen(name));
|
||||
|
||||
return variableSlot != -1;
|
||||
}
|
||||
|
||||
bool wrenHasModule(WrenVM* vm, const char* module)
|
||||
{
|
||||
ASSERT(module != NULL, "Module cannot be NULL.");
|
||||
|
||||
Value moduleName = wrenStringFormat(vm, "$", module);
|
||||
wrenPushRoot(vm, AS_OBJ(moduleName));
|
||||
|
||||
ObjModule* moduleObj = getModule(vm, moduleName);
|
||||
|
||||
wrenPopRoot(vm); // moduleName.
|
||||
|
||||
return moduleObj != NULL;
|
||||
}
|
||||
|
||||
void wrenAbortFiber(WrenVM* vm, int slot)
|
||||
{
|
||||
validateApiSlot(vm, slot);
|
||||
|
||||
@ -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
|
||||
{
|
||||
@ -243,4 +243,9 @@ static inline bool wrenIsLocalName(const char* name)
|
||||
return name[0] >= 'a' && name[0] <= 'z';
|
||||
}
|
||||
|
||||
static inline bool wrenIsFalsyValue(Value value)
|
||||
{
|
||||
return IS_FALSE(value) || IS_NULL(value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -32,6 +32,25 @@ static void otherModule(WrenVM* vm)
|
||||
wrenGetVariable(vm, "./test/api/get_variable_module", "Variable", 0);
|
||||
}
|
||||
|
||||
static void hasVariable(WrenVM* vm)
|
||||
{
|
||||
const char* module = wrenGetSlotString(vm, 1);
|
||||
const char* variable = wrenGetSlotString(vm, 2);
|
||||
|
||||
bool result = wrenHasVariable(vm, module, variable);
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotBool(vm, 0, result);
|
||||
}
|
||||
|
||||
static void hasModule(WrenVM* vm)
|
||||
{
|
||||
const char* module = wrenGetSlotString(vm, 1);
|
||||
|
||||
bool result = wrenHasModule(vm, module);
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotBool(vm, 0, result);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn getVariableBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static GetVariable.beforeDefined()") == 0) return beforeDefined;
|
||||
@ -39,6 +58,9 @@ WrenForeignMethodFn getVariableBindMethod(const char* signature)
|
||||
if (strcmp(signature, "static GetVariable.afterAssigned()") == 0) return afterAssigned;
|
||||
if (strcmp(signature, "static GetVariable.otherSlot()") == 0) return otherSlot;
|
||||
if (strcmp(signature, "static GetVariable.otherModule()") == 0) return otherModule;
|
||||
|
||||
if (strcmp(signature, "static Has.variable(_,_)") == 0) return hasVariable;
|
||||
if (strcmp(signature, "static Has.module(_)") == 0) return hasModule;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -8,6 +8,11 @@ class GetVariable {
|
||||
foreign static otherModule()
|
||||
}
|
||||
|
||||
class Has {
|
||||
foreign static variable(module, variable)
|
||||
foreign static module(module)
|
||||
}
|
||||
|
||||
System.print(GetVariable.beforeDefined()) // expect: null
|
||||
|
||||
var A = "a"
|
||||
@ -22,3 +27,13 @@ var B = "b"
|
||||
System.print(GetVariable.otherSlot()) // expect: b
|
||||
|
||||
System.print(GetVariable.otherModule()) // expect: value
|
||||
|
||||
|
||||
System.print(Has.variable("./test/api/get_variable_module", "Variable")) // expect: true
|
||||
System.print(Has.variable("./test/api/get_variable_module", "NotAVariable")) // expect: false
|
||||
System.print(Has.variable("./test/api/get_variable", "Has")) // expect: true
|
||||
System.print(Has.variable("./test/api/get_variable", "Fake")) // expect: false
|
||||
|
||||
System.print(Has.module("./test/api/get_variable_module")) // expect: true
|
||||
System.print(Has.module("./test/api/get_variable")) // expect: true
|
||||
System.print(Has.module("not a module")) // expect: false
|
||||
@ -15,6 +15,14 @@ static void insertNumber(WrenVM* vm, int index, double value)
|
||||
wrenInsertInList(vm, 0, index, 1);
|
||||
}
|
||||
|
||||
// Helper function to append a double in a slot then insert it into the list at
|
||||
// slot zero.
|
||||
static void appendNumber(WrenVM* vm, double value)
|
||||
{
|
||||
wrenSetSlotDouble(vm, 1, value);
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
}
|
||||
|
||||
static void insert(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
@ -37,10 +45,40 @@ static void insert(WrenVM* vm)
|
||||
insertNumber(vm, -3, 9.0);
|
||||
}
|
||||
|
||||
static void get(WrenVM* vm)
|
||||
{
|
||||
int listSlot = 1;
|
||||
int index = (int)wrenGetSlotDouble(vm, 2);
|
||||
|
||||
wrenGetListElement(vm, listSlot, index, 0);
|
||||
}
|
||||
|
||||
static void set(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
|
||||
wrenEnsureSlots(vm, 2);
|
||||
|
||||
appendNumber(vm, 1.0);
|
||||
appendNumber(vm, 2.0);
|
||||
appendNumber(vm, 3.0);
|
||||
appendNumber(vm, 4.0);
|
||||
|
||||
//list[2] = 33
|
||||
wrenSetSlotDouble(vm, 1, 33);
|
||||
wrenSetListElement(vm, 0, 2, 1);
|
||||
|
||||
//list[-1] = 44
|
||||
wrenSetSlotDouble(vm, 1, 44);
|
||||
wrenSetListElement(vm, 0, -1, 1);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn listsBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Lists.newList()") == 0) return newList;
|
||||
if (strcmp(signature, "static Lists.insert()") == 0) return insert;
|
||||
if (strcmp(signature, "static Lists.set()") == 0) return set;
|
||||
if (strcmp(signature, "static Lists.get(_,_)") == 0) return get;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
class Lists {
|
||||
foreign static newList()
|
||||
foreign static insert()
|
||||
foreign static set()
|
||||
foreign static get(list, index)
|
||||
}
|
||||
|
||||
var list = Lists.newList()
|
||||
@ -8,3 +10,7 @@ System.print(list is List) // expect: true
|
||||
System.print(list.count) // expect: 0
|
||||
|
||||
System.print(Lists.insert()) // expect: [4, 5, 6, 1, 2, 3, 9, 8, 7]
|
||||
|
||||
System.print(Lists.set()) // expect: [1, 2, 33, 44]
|
||||
System.print(Lists.get([1,2,3,4], -2)) // expect: 3
|
||||
System.print(Lists.get([1,2,3,4], 1)) // expect: 2
|
||||
|
||||
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 removeKey(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 removeKey;
|
||||
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);
|
||||
64
test/api/maps.wren
Normal file
64
test/api/maps.wren
Normal file
@ -0,0 +1,64 @@
|
||||
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
|
||||
@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user