475 Commits

Author SHA1 Message Date
7afc0e4a9c debugger; add note about less overhead version 2022-06-06 15:30:32 -07:00
092fd90a3a debugger; add credits to notes 2022-06-06 15:11:00 -07:00
ef0770fe45 add original todo notes from @keymaster- 2022-06-06 15:08:37 -07:00
7469b76fb7 debugger; update notes 2022-06-06 15:04:29 -07:00
3454ae0465 initial debugger commit - most work contributed by @KeyMaster- as part of luxe engine! 2022-06-06 14:56:07 -07:00
4ffe2ed38b Fix formatting error (#1078)
Fixed error that caused `<stdio.h>` to disappear from C `#include` directive
2022-01-31 15:43:01 -08:00
77079f2f49 CI: artifacts 2021-11-14 01:00:19 -08:00
2adb220686 CI: add initial github actions 2021-11-14 00:50:43 -08:00
accfa598b3 Allow people to share links to the playground (#1046)
* Allow people to share links to the playground

We do that by encoding the code (compressed) in the URL.

The compression is the same used in the TypeScript playground:
e9d8f66f6b/packages/sandbox/src/compilerOptions.ts (L119-L120)
2021-08-09 17:41:00 -07:00
55e8807aba fix #1039 clarity in header comments 2021-08-09 17:24:47 -07:00
dd1e8a00db docs; values; add note about newline normalization in docs 2021-05-21 16:30:08 -07:00
3c43de7485 minor consistency fixes 2021-05-21 16:24:34 -07:00
59e496e26a Fix CRLF being significant inside of strings, now strings are normalized to \n only and the same output regardless of the file line endings (see #988)
https://github.com/wren-lang/wren/issues/988#issuecomment-842670624
2021-05-21 16:24:12 -07:00
d78f481079 wren_core.wren.inc backslash characters escaped (#1023) 2021-05-21 16:09:00 -07:00
9903ddf76b docs; string; clarify indexOf(_,_) to be clearer 2021-05-16 12:17:31 -07:00
f09ebf6acc fix attributes causing crash when GC is triggered by compiler init 2021-05-05 14:34:16 -07:00
7fb7f6430d minor 2021-05-05 14:29:39 -07:00
aca7c70079 docs; fix example with 'continue' (#1007) 2021-05-04 14:56:53 -07:00
eca90e29e9 Document the Meta class. (#977) 2021-04-16 11:12:32 -07:00
a346494922 Documents 'return from module'. (#976)
Fixes #974.
2021-04-16 11:09:28 -07:00
754d5a59e9 correct attributes example (#978) 2021-04-16 11:08:01 -07:00
8eb51d9327 doc: fix links from Object to values (#975)
Fix two broken links from the documentation for the Object class to the
page describing values.
2021-04-15 21:29:26 -07:00
22ff3b5549 update try to 0.4.0 2021-04-09 12:48:39 -07:00
e4b4df2e62 minor; remove redundant comment bit 2021-04-09 11:17:15 -07:00
46b06f0620 minor formatting fix 2021-04-08 22:22:34 -07:00
d650b60c25 0.4.0 release - fix links and formatting 2021-04-08 22:17:13 -07:00
30c9f1ee37 initial 0.4.0 blog post 2021-04-08 22:09:02 -07:00
4a18fc489f tests; maps; remove test that now asserts in debug
for now, as there doesn't seem to be a way to test for that , it fails CI
2021-04-08 21:46:31 -07:00
d763c72e3d fix use of NULL_VAL, also invalid when nan tagging is disabled 2021-04-08 21:40:06 -07:00
34b01226a4 fix use of UNDEFINED_VAL which is invalid when nan tagging is disabled 2021-04-08 21:34:27 -07:00
a4ae905384 Introduce Attributes (#962)
* introduce Attributes for classes and methods
2021-04-08 21:30:09 -07:00
5244a9d001 Add an additional escape sequence \e. (#963) 2021-04-08 10:16:24 -07:00
9e86b0d26a clarify the list of escapes to show the whole list 2021-04-08 10:11:01 -07:00
e260b467c4 made wren version number accessable via function (#958) 2021-04-08 09:33:22 -07:00
1307bdfb64 value: Rename validateKeyTypeto wrenMapIsValidKey. (#965) 2021-04-08 09:10:55 -07:00
06b71897b0 Surface error messages for map key values in the API with asserts
closes https://github.com/wren-lang/wren/pull/921
2021-04-07 23:08:31 -07:00
059e407ed3 wren/compiler: Allow multiline empty parameter lists and calls. (#925) 2021-04-07 22:58:46 -07:00
fd1d095588 Add preamble for Timer module (#920)
Opening remarks also needed here.
2021-04-07 22:56:51 -07:00
7a131a67e6 Add preamble for Scheduler module (#919)
We need some opening remarks here to get rid of the TODO.
2021-04-07 22:56:29 -07:00
e4052a25d7 wren/vm: Add wrenIsFalsyValue. (#866) 2021-04-07 22:54:49 -07:00
041f1bab8d Fix returning from constructors (#845)
* Fix returning from constructors

 1. Do not allow returning with a value
 2. Return the instance, correctly, even when the user returned explicitly

* revise error message for consistency, revise implementation details a bit, fix extra args to finishBody

* clarify tests a bit

* document constructor return

Co-authored-by: ruby0x1 <ruby0x1@pm.me>
2021-04-07 22:53:05 -07:00
68f5c096d8 Disallow non-Num arguments in Num for min(_), max(_), clamp(_,_), pow(_) and atan(_) (#859)
* Disallow non-Num arguments in `Num.min(_)`, `Num.max(_)`, `Num.clamp(_,_)`

Previously this was an Undefined Behavior

* also validate args for pow, atan2, add tests, fix error messages

Co-authored-by: ruby0x1 <ruby0x1@pm.me>
2021-04-07 22:04:58 -07:00
4847b37789 minor cleanup 2021-04-07 21:56:19 -07:00
28da4b449c Add Num.[max/min]SafeInteger (#874) 2021-04-07 21:45:00 -07:00
61cc6cb745 more minor doc fixes 2021-04-07 21:37:12 -07:00
0ab930c9c2 reorder some docs a bit, fix missing constructor 2021-04-07 21:21:50 -07:00
d38c047a5a documentation revisions and missing pieces 2021-04-07 20:54:18 -07:00
0be504832e fix code documentation 2021-04-07 18:36:11 -07:00
4cd374e1a7 fix amalgamation documentation 2021-04-06 19:59:42 -07:00
5e60bbf0cf document string range indexing for substring 2021-04-06 19:56:28 -07:00
197c0ff4f9 fix loadModuleFn description in header 2021-04-06 19:49:40 -07:00
e3c76a3e76 Raw strings now ignore whitespace on both ends for consistency and clarity
added more tests, updated documentation
2021-04-04 22:28:57 -07:00
8304fd5ecc docs; strings; fix formatting for example 2021-04-04 14:02:07 -07:00
ea684194db try; update for testing latest 2021-04-04 13:54:58 -07:00
981ea4adf1 Add raw string literals
Also document + test multi line literals.
2021-04-04 13:52:32 -07:00
345f919e26 make WREN_API_DLLEXPORT required, fixes statically linked versions
-_-
2021-04-04 13:29:32 -07:00
853f5a3414 fix link in fiber page 2021-04-04 12:31:45 -07:00
11bea3ca01 Add documentation for Fiber.transfer methods (#949) 2021-04-04 12:20:15 -07:00
33ab8be7e3 fix possibility of having no reallocate function (#954)
Before, if a config was provided it was expected to have a reallocate function, now it can remain null and the default will be used.
2021-04-04 11:24:48 -07:00
a501fba4bb add WREN_API declaration for proper symbol export
Brought up by @Orcolom

Detected for MSVC or similar and can be explicitly activated by defining WREN_API_DLLEXPORT (or manually defining WREN_API to __declspec( dllexport ) before include).

Can be disabled by using a blank `#define WREN_API` before include.

Note: to use the import variant, `#define WREN_API __declspec(dllimport)` before including the wren header.
2021-04-04 11:18:40 -07:00
615a6aa208 try; fix random and meta properly 2021-04-03 22:09:19 -07:00
94e835cdd6 minor clean up of try readme 2021-04-03 22:02:13 -07:00
3c0fe12102 try; add try implementation + details, update to latest main branch 2021-04-03 21:58:31 -07:00
ecce1f6be9 List; add remove(value)
Having to encode this behaviour at every call site is tedious. It makes a lot of sense to just have the method available on list itself.
2021-04-03 19:55:42 -07:00
4d1d0d972e rename some test functions that clash with libc (#936)
* rename some test functions that clash with cosmopolitan libc
2021-03-07 16:38:29 -08:00
5b290cacc5 Tentative fix for Num class docs issue on the wren.io site. (#932) 2021-03-02 11:11:21 -08:00
dfa7d9c895 doc/site/functions Fn.new() clarification [minor] (#931)
In the description of Fn.new():
- Fix a typo
- Add the referent of "that" for clarity
2021-03-02 09:27:23 -08:00
2bc895c26d added documentation on * operator for strings (#930) 2021-02-18 19:16:09 -08:00
208bbc02a5 revert #826
3a06580b89
2021-02-13 10:17:59 -08:00
9dfbc021a0 wren/compiler: Store value in the correct token. (#923) 2021-02-06 07:53:39 -08:00
ae6fdb3da7 Add docs for newly merged (#905) Num.cbrt method (#918) 2021-01-31 10:22:19 -08:00
3a06580b89 Don't use strtoll() with explicit base to parse hex, instead use strtod() (#826)
`strtod()` automatically recognizes the `0x` and converts to hex

This has two advantages:
 1. Simplify code (no need of argument `isHex` to `makeNumber`, no `if`)
 2. Allow numbers larger than `long long` if they fit into `double`
2021-01-30 21:44:52 -08:00
0fa16a20ec Fix deprecated Python timing in benchmark & give more error info. (#844)
- Note: process_time gives CPU time used and perf_counter is absolute time used.
- Looks to have noise of about 1-2%.
2021-01-30 21:40:20 -08:00
af5227f03b Mention List.[Range] in the docs (#870) 2021-01-30 21:34:27 -08:00
1720a20979 In List.+(other), other should be an iterable, list is not required (#872)
We're iterating over it but do not check it's actually a list:

cb51d61a64/src/vm/wren_core.wren (L363-L369)
2021-01-30 21:33:54 -08:00
4ac41c36db Added hexadecimal number literals example (#876) 2021-01-30 21:32:42 -08:00
79000a320e Proposed additions to Map class docs. (#888)
The Map class documentation is missing a few details which I think should ideally be covered. The proposed changes should be self evident.
2021-01-30 21:22:54 -08:00
f1225ef7dd Add docs for remaining bitwise operators (#887)
I mentioned in #881 that the `^`, `<<` and `>>' bitwise operators were currently undocumented so this fixes that.

Have also added a note to Num.tau to clarify that this means `twice pi `and is not the golden ratio for which `tau` is sometimes used as an alternative to `phi`.
2021-01-30 21:21:59 -08:00
57bebd41ca Adds documentation for System.writeAll method (#886) 2021-01-30 21:21:33 -08:00
0f8b44e61b Fix undocumented methods in the Num class ( #879) (#884) 2021-01-30 21:21:18 -08:00
96eb68bae3 Fix undocumented methods in the List class ( #878) (#883)
Also noted that the ```add``` method returns the added item.
2021-01-30 21:21:07 -08:00
16ddbb66f8 Add docs for Scheduler class (#902)
I think it's time we had a go at this.
2021-01-30 21:15:56 -08:00
3ceb029df9 Add docs for Timer class (#903)
Another class that has remained undocumented for a long time.
2021-01-30 21:15:22 -08:00
ce599259bc wren/core: Add Num::cbrt. (#905) 2021-01-30 21:13:58 -08:00
97bc340737 Happy new year! (#904) 2020-12-31 18:52:09 -08:00
81aff84415 Fix incorrect heading (#871)
There was a space instead of `#`
2020-12-13 20:48:18 -08:00
cb51d61a64 fix unmarked compiler value causing memory issues
https://github.com/wren-lang/wren/issues/869
2020-12-07 12:34:31 -08:00
9da1e265df Remove unused field skipNewlines (#858) 2020-12-03 19:53:26 -08:00
9fbfe6a419 Add slashes before split comment line (#861) 2020-12-03 19:52:17 -08:00
3c07611b0a Syntax-highlight continue keyword (#864) 2020-12-03 19:51:48 -08:00
aac6296317 Add continue to the list of reserved words (#865) 2020-12-03 19:50:56 -08:00
a294da7974 Only the method name should be bold, not including the parameters list (#862) 2020-12-03 19:50:21 -08:00
c572345c3c &infinity; is not a valid HTML symbol - use &infin; (#863) 2020-12-03 19:48:49 -08:00
da3a3f0e0e Update draft 0.4.0 changelog 2020-12-03 14:32:53 -08:00
7d3f063e87 Merge branch 'next-token' into main 2020-12-03 13:43:40 -08:00
4687300ad6 fix forward declaration using c11 features (???) 2020-12-03 13:38:46 -08:00
89c5e22480 add Num.tau 2020-12-03 13:20:15 -08:00
8361217369 Num; add min, max and clamp 2020-12-03 13:18:13 -08:00
38f50fe091 List; add swap(index0, index1) 2020-12-03 13:17:53 -08:00
62009870a8 List; add indexOf(value) 2020-12-03 13:17:26 -08:00
3d5e68fc01 Added List.sort(comp) to List module (#802) 2020-12-03 11:59:07 -08:00
08d2fa3821 fix paste issues from https://github.com/wren-lang/wren/pull/798/ 2020-12-03 11:55:31 -08:00
d8bdc7359e Added myself to the authors file (#856) 2020-12-03 11:51:48 -08:00
76fb4f311b Test static operators (#798)
In #797 it was stated that static operators are valid in Wren, and proposed to check this behavior
2020-12-03 11:47:42 -08:00
84b29e6995 Add userData pointer to reallocateFn (#788)
* Add userData ptr to all reallocateFn calls
* Check that userData is correctly passed
* Update AUTHORS
2020-12-03 11:46:22 -08:00
a11d66cbd3 WIP wren/core: Add DEF_NUM_CONSTANT (with Num::infinity and Num::nan). (#781)
* wren/vm: Add "wren_math.h".
* wren/core: Add DEF_NUM_CONSTANT.
* wren/core: Add `Num::infinity` constant.
* wren/core: Add `Num::nan` constant.
2020-12-03 11:37:53 -08:00
59ee326523 Added a quick explanation of the continue keyword (#854)
* Added a quick explanation of the continue keyword
2020-12-03 11:29:52 -08:00
182ca90b8c add wrenHasVariable and wrenHasModule 2020-12-03 11:13:04 -08:00
bc7dd50a54 fix warning 2020-12-03 10:47:49 -08:00
97ebcc72c3 Add wrenSetListElement, correctly allow negative indices on wrenGetListElement 2020-12-03 10:30:47 -08:00
999acba06f bump version to 0.4.0, since that's the one we're working on 2020-12-03 09:41:27 -08:00
5264b46246 Fix outdated comment 2020-12-03 09:40:56 -08:00
3e0f71b742 [0.4.0] Import as (#775)
* Add import "..." for Variable as OtherName
2020-12-03 09:34:36 -08:00
6bd2f810e2 [0.4.0] Introduce WrenLoadModuleResult, fix unfreed strings from host. (#778) 2020-12-03 09:27:54 -08:00
e7071fffa5 Update AUTHORS (#855)
@ruby0x1 suggested I add my name to the AUTHORS file
2020-12-03 08:58:46 -08:00
55b926410d Add continue statement (#822)
Note that documentation is still required.
2020-12-03 08:30:36 -08:00
f5339993ce Add support for Fiber.try(_) (#835)
* Add support for Fiber.try(_)
* Add documentation for Fiber.try(_)
* Add another test for Fiber.try(_)
2020-12-03 08:21:37 -08:00
473392a56a docs: fix simple typo, similiar -> similar (#843)
There is a small typo in src/vm/wren_value.h.

Should read `similar` rather than `similiar`.
2020-12-03 08:19:19 -08:00
556eeac86e Fix broken link (#799)
The Variables guide linked to `/modules.html`, but the page talking about modules is `/modularity.html`
2020-11-25 22:49:52 -08:00
94e4888b6a Fix link in null.markdown (#848) 2020-11-25 21:09:37 -08:00
44d6d20586 Do not allow inheriting built-in classes Num, Bool and Null (#831)
* Do not allow inheriting `Num`, `Bool` and `Null`. fixes #830
2020-10-26 08:39:36 -07:00
ad4e039187 Fix svg link to travis build (#828) 2020-10-17 15:57:39 -07:00
3c475f01ee allow newline before dot for subscript as well, and add to tests 2020-09-19 22:03:16 -07:00
4c496c56a6 allow a newline before dot usage, for chained/fluent interfaces 2020-09-19 20:40:24 -07:00
1c5ac28831 compiler now tracks next token (in addition to current/previous) 2020-09-19 20:40:24 -07:00
45c67fae0c Fn call: move arity check into interpret loop, which avoid the expensive if after the call, since runtime errors originating inside the call itself will still be handled, we only have the one emitted from call itself.
This brings the benchmark back up to where it was.
2020-09-18 15:42:37 -07:00
beae242a41 vm; handle errors from fn.call (still investigating) 2020-09-18 13:11:12 -07:00
86463acb90 Fix stack corruption caused by Fn call primitives (#807)
Excerpt from @munificent on the nature of the bug:

In runInterpreter, for performance, the vm caches an IP pointing into some bytecode.

All primitives except for `.call`, do not touch Wren's own callstack. They run a little C code and return, so the array of CallFrames, their IPs, and the IP cached inside run() are not affected at all.

While runInterpreter() is running, the IP in the top CallFrame is not updated, so it gets out of sync. This is deliberate, since storing to a field is slow, but it means the value of that field is stale and doesn't represent where execution actually is at that point in time.

To get that field in sync, we use STORE_FRAME(), which stores the local IP value back into the IP field for the top CallFrame. The interpreter is careful to always call STORE_FRAME() before executing any code that pushes a new CallFrame onto the stack.

In particular, if you look around, you'll see that every place the interpreter calls wrenCallFunction() is preceded by a STORE_FRAME(). That is, except for the call to wrenCallFunction() in the call_fn() primitive. That's the bug.

The .call() method on Fn is special because it does modify the Wren call stack and the C code for that primitive directly calls wrenCallFunction(). When that happens, the correct IP for the current function, which lives only in runInterpreter()'s local variable gets discarded and you're left with a stale IP in the CallFrame.

Giving the function call primitives a different method type and having the case for that method type call STORE_FRAME() before invoking the primitive fixes the bug.
2020-09-18 12:32:43 -07:00
f769599bc6 docs; fix random module docs missing a closing tag (thanks @totallyRonja) 2020-08-29 12:03:46 -07:00
039150efeb docs; fix example formatting 2020-07-30 09:24:23 -07:00
81bfbfce23 fix issue with docs clearing the writeFn from the config. add complete embedding example to docs + repo 2020-07-30 09:09:15 -07:00
dead8df82e GC debug times are easier to reason about when printed in milliseconds, rather than seconds 2020-07-18 20:50:26 -07:00
b279e51fd1 Allow computed goto when using clang on Windows
Clang defines _MSC_VER for compatibility with MSVC, but that can often create problems for code that assumes MSVC only.
2020-07-18 20:41:16 -07:00
286162365a update wip 0.4.0 changelog 2020-07-18 20:26:23 -07:00
5b0f8740f2 Revert "Extended test for Random.sample to cover both branches (#715)"
This reverts commit f81cb5d23c.
2020-07-18 20:10:22 -07:00
f81cb5d23c Extended test for Random.sample to cover both branches (#715)
I've verified that this test fails without the fix in change
186a8c7c13.

See issue #713
2020-07-14 20:15:12 -07:00
54b4c233b9 test: Fix some tests so they fail on expected error, not on syntax error. (#779) 2020-07-14 20:14:08 -07:00
58611240e7 Remove magic values as exit codes in test application (#777) 2020-07-11 13:30:43 -07:00
a3f5b3d98f wren/vm: Allow wrenInterpret to call foreign function (complement 344d343 at fixing #730). (#764) 2020-07-11 13:05:22 -07:00
da091e250c set WREN_MAX_TEMP_ROOTS default to 8 instead of 5
that's 64 bytes, fits nicely in a cache line and isn't _as_ arbitrary.
2020-07-11 11:34:35 -07:00
2ce421eac5 use push root instead of a handle for module GC protection
related to d432b03d62
2020-07-11 11:30:52 -07:00
d432b03d62 fix many module imports causing GC to pull the rug on our module instance 2020-07-10 20:00:17 -07:00
433fbc4019 core; num; add exp & log2
I've had a couple use cases in time that the code is significantly clearer with these, and makes porting less error prone
2020-07-10 19:38:45 -07:00
0e8d56f874 add a note about switched goto for future reference 2020-07-10 19:15:21 -07:00
28ad8aa9e0 compiler; fix incorrect byte length for CODE_IMPORT_VARIABLE
this can lead to some REALLY fun debugging because various code bytes/instructions get skipped, leading to wrong inputs into wrong opcodes and all sorts 💯
2020-07-10 19:14:54 -07:00
b3d496ea36 compiler; rename getNumArguments to be clearer as to the intent 2020-07-10 19:13:44 -07:00
8be40ec14e runFile: Free file source before exiting the function (#774)
This prevents a memleak, noticeable when running `wren_test` under
`valgrind`. For example, the following command would leak

`./bin/wren_test_d any_example.wren`
2020-07-09 11:54:21 -07:00
1623654465 docs; fix embedding tutorial missing a write function so nothing shows up when learning. 2020-06-30 07:40:12 -07:00
e539279121 docs; fix // comments properly this time
...
2020-06-17 09:48:12 -07:00
7651459dfb add // comments to code highlighting, but not with errors in the code 2020-06-17 09:36:34 -07:00
d02903b7d0 add // comments to code highlighting 2020-06-17 09:20:15 -07:00
88043a7cb9 Change wren to wren_cli (#765) 2020-06-17 07:57:03 -07:00
b59c060ccd Small tweaks to error handling. (#762)
* wren/primitive: Remove duplicated declaration introduced in 9f64c05fa.
* wren/primitive: Allow RETURN_ERROR_FMT to have any number of arguments.
* wren/vm: Remove extra validateApiSlot in wrenGetVariable.
(The slot validation is guaranted by setSlot later in the function.)
* wren/primitive: Use RETURN_ERROR_FMT in validateFn.
2020-06-15 11:31:29 -07:00
30b2ebd3f7 fix util/generate_project.py
- fix premake args being incorrect
- remove platform assumptions, making it portable
- start with a best guess based on project layout
- use fallback if not specified or not found
- display errors/help if not found
2020-06-14 19:44:58 -07:00
de6a312868 Functions for operating on Maps from C (#725)
new API functions for maps:
wrenSetSlotNewMap
wrenGetMapCount
wrenGetMapContainsKey
wrenGetMapValue
wrenSetMapValue
wrenRemoveMapValue
2020-06-14 14:45:23 -07:00
344d3432b3 Fix slot array corrupted by wrenInterpret() (#730) 2020-06-13 21:42:06 -07:00
7983082b71 Support positive sign in scientific notation (#706)
* Support positive sign in scientific notation
* Add exponent with positive sign to docs
2020-06-13 21:37:30 -07:00
f3493d0499 Optimize Random.sample(_, _) for performance (#716)
* Optimize Random.sample(_, _) for performance
* Make tests treat random samples as unordered
* Test all sample sizes possible
* Tweak random sampling algorithm for performance
2020-06-13 21:31:23 -07:00
fea0dfafa0 Fix typos in wren_value.h (#749)
* Fix typo: "pointing too" -> "pointing to"
* Fix typo: It *is* heap-allocated
2020-06-13 21:25:55 -07:00
f894273f50 Refactor travis script (#754) 2020-06-13 21:25:18 -07:00
909d1c9471 Add script to regenerate projects using premake (#755) 2020-06-13 21:24:55 -07:00
b5894c6ff5 wren: Remove a magic number in the compiler. (#757) 2020-06-13 21:24:37 -07:00
7c357e1b02 Update gitignore (#759)
Add VSCode project folder and macOS specific stuff.
2020-06-13 21:24:16 -07:00
9fb6d02b5c main branch reference fixes 2020-06-12 10:11:49 -07:00
e45a9d0382 removed localhost (#758) 2020-06-09 17:03:36 -07:00
bef4099101 missed nitpick 2020-06-08 12:31:03 -07:00
9f64c05fa8 Make do blocks in macros consistent with the rest of the code 2020-06-08 12:28:15 -07:00
26d0194117 wren/vm: Uniformize macros to stick to 80 columns. (#756)
uniform macros
2020-06-08 12:23:15 -07:00
2c2f5936eb tests; warn against missing wren_test binary correctly 2020-06-06 10:38:43 -07:00
b694b2231c docs; fix blog template missing links and wrong paths 2020-06-06 10:32:44 -07:00
6cfe6dd6de tests; warn against missing wren_test binary instead of a loud error 2020-06-06 10:29:35 -07:00
8341f61cdb docs; nitpicks 2020-06-05 20:11:32 -07:00
cd01246997 remove pygments setup 2020-06-05 15:21:33 -07:00
6350ddd242 fix permissions for travis doc script 2020-06-05 15:16:59 -07:00
d69d7e482d Added description of to the Num class docs (#726) 2020-06-05 15:08:52 -07:00
7b56dce4f0 Fix example C code (#745) 2020-06-05 15:08:25 -07:00
d585a080e8 Merge pull request #751 from wren-lang/0.3.0-refactor
0.3.0 refactor
2020-06-05 14:57:20 -07:00
6ab4abe9e3 cli; Fix a memory leak in findModulesDirectory() (#718) 2019-12-27 10:42:32 -08:00
186a8c7c13 Fixed Random.sample(list, count) for small number of samples (#714)
Closes #713
2019-12-11 15:40:07 -08:00
53cf6f511b benchmark; fix parsing of None in certain cases (like with no interpreter when a baseline is made) 2019-10-07 23:48:42 -07:00
b120739631 fix minor warning 2019-10-07 23:43:05 -07:00
51ef6ff5fa docs; fix missing blog link and arrangement in small mode
thanks Brian Slesinsky
2019-10-02 23:41:39 -07:00
faff0d6ca4 blog; formatting 2019-09-30 23:03:45 -07:00
8ce631e80e blog; add 0.2.0/0.3.0 post 2019-09-30 22:48:04 -07:00
5338275dcd 0.2.0 prep work 2019-09-30 21:38:08 -07:00
3eb4ac1419 Add a clearer error message for forward declared lowercase variables (#699)
* Add better error messaging for forward referenced top level variables, see #467
* add test case
2019-09-30 20:30:27 -07:00
fc7bd1c78f Merge pull request #703 from mwilmsm/master
Cleaned up some documentation typos. -mw
2019-09-30 13:23:51 -07:00
265bcc6780 Cleaned up some documentation typos. -mw 2019-09-30 09:02:55 -05:00
ff4f92eb87 .gitignore; and one more 2019-09-28 12:21:45 -07:00
7617498aa3 .gitignore; correct path for object files 2019-09-28 12:21:19 -07:00
688691b17c Don't access null pointers when printing null module names
fixes #631
2019-09-18 22:49:01 -07:00
7eac8e6a37 docs; encapsulation legibility fixes 2019-09-18 01:43:34 -07:00
898e5356c7 Merge pull request #562 from Salmela/locals-array-overflow-fix
Check that there is space in local variable array
2019-09-18 00:20:03 -07:00
f3b0200c39 Merge pull request #541 from kext/amalgamation
Repaired amalgamation script
2019-09-18 00:11:22 -07:00
cdeb0db5c3 disambiguate the call here from the call in wren_compiler.c (fixes amalgamation having overalapped symbols) 2019-09-18 00:08:07 -07:00
123ba80a89 fix warning about conversion from '__int64' to 'double', possible loss of data 2019-09-18 00:05:16 -07:00
fa2a3d9576 Clarify modulus documentation for sign
closes #695
2019-09-17 22:27:47 -07:00
0e67667010 Update Encapsulation documentation to be clearer
fixes #691
2019-09-17 22:18:55 -07:00
e94c813ecc Merge pull request #698 from matusnovak/fix-foreign-allocate-abort-fiber
Fix wrenAbortFiber does not work inside of foreign class allocator
2019-09-17 11:08:37 -07:00
f91586a71a Fix wrenAbortFiber does not work inside of foreign class allocator 2019-09-17 19:43:53 +02:00
740c365597 Merge pull request #693 from wren-lang/faster-bit-hash
Fix horrendously bad bit hashing function.
2019-07-27 21:00:02 -07:00
2a1499b04b Fix horrendously bad bit hashing function.
hashBits() is used to generate a hash code from the same 64 bits used
to represent a Wren number as a double. When building a map containing
a large number of integer keys, it's important for this to do a good
job scattering the bits across the 32-bit key space.

Alas, it does not. Worse, the benchmark to test this happens to stop
just before the performance falls off a cliff, so this was easy to
overlook.

This replaces it with the hash function V8 uses, which has much better
performance across the numeric range.
2019-07-27 13:34:07 -07:00
d1a0d0682a Merge pull request #688 from pyway/patch-1
fix memory leak in path.c
2019-07-01 07:40:14 -07:00
vvn
5725ecad67 Update path.c 2019-06-13 14:51:28 +08:00
a8fd838255 Merge pull request #680 from bjorn/patch-1
docs: Fixed broken link to overview about maps
2019-04-09 15:46:38 -07:00
451098db05 docs: Fixed broken link to overview about maps
Closes #672
2019-04-09 17:44:15 +02:00
13dfebe1a5 Merge pull request #664 from Heaven31415/master
Update note about path to visual studio solution.
2019-03-24 14:09:24 -07:00
fe3aa60601 Update note about path to visual studio solution. 2019-03-24 21:36:21 +01:00
2d018c9109 Merge pull request #663 from Heaven31415/master
Update link to comment about NaN tagging.
2019-03-24 08:03:52 -07:00
51230e18fc Update link to comment about NaN tagging. 2019-03-24 12:46:39 +01:00
93dac91327 Merge pull request #660 from walterschell/feature/string_fromByte
Added String.fromByte with docs and unit test
2019-02-28 19:31:26 -08:00
a437e804ba Added String.fromByte with docs and unit test 2019-02-27 08:10:54 -05:00
487f53ace1 Merge pull request #657 from SuperWangKai/master
Fixed compiling error of cli on Windows
2019-02-20 09:00:22 -08:00
kai
c58e51fa96 Fixed compiling error of cli on Windows 2019-02-19 15:14:53 +08:00
1ec0b649c1 Merge pull request #589 from FICTURE7/master
Renamed include guard in wren_compiler.h
2019-02-15 07:30:46 -08:00
51c857ab0e Merge pull request #610 from jyaif/bar
Remove unused prototype.
2019-02-15 07:30:16 -08:00
abbb410ff2 Fix assert in insertEntry().
The intent of the assert is to ensure that insertEntry() is not called
with an empty entries array because that would cause you to get into a
code path where the entry output parameter is not set. But the assert
didn't correctly check that.

Fix #635.
2019-02-14 07:51:57 -08:00
03d70f336f Merge pull request #620 from PrimordialHelios/fixup_overflow
Fix overflow on line 401 of wren_compiler.c
2019-02-14 07:42:12 -08:00
b2a2494008 Regenerate REPL include. 2019-02-14 07:33:30 -08:00
35df7faa1e Merge branch 'repl_keys' of https://github.com/PrimordialHelios/wren into PrimordialHelios-repl_keys
# Conflicts:
#	AUTHORS
2019-02-14 07:31:22 -08:00
0f8e9a6554 Merge pull request #612 from iwillspeak/osx-buildfixes
Disable 32 bit macOS Build
2019-02-11 23:33:05 -08:00
6514c592d3 Merge pull request #611 from iwillspeak/doc-fixup
Update Call to `WrenInterpret` in Docs
2019-02-11 23:32:40 -08:00
8a0d2297a1 Merge pull request #615 from iwillspeak/repl-escape-fix
Fixup Handling of `\` in REPL to Prevent Crash
2019-02-11 22:58:06 -08:00
ccb9ba3441 Disable 32 bit macOS Build
With the latest XCode it looks like 32 bit builds are deprecated. This
causes build warnings which fail the libuv build. This commit remvoves
the 32 bit arch from the macOS libuv build, and stops building the 32
build on Travis.
2019-02-12 06:56:38 +00:00
9347c5c61a Update Call to WrenInterpret in Docs
Fixes #599 by updating the arguments to WrenInterpret to match the new
API.
2019-02-12 06:54:58 +00:00
52c16b4f80 Merge branch 'wrenHasError' of https://github.com/mhermier/canary into mhermier-wrenHasError 2019-02-11 07:52:38 -08:00
a945bed812 Merge pull request #655 from wren-lang/limited-reentrancy
Add a limited form of re-entrant calls.
2019-02-11 07:38:09 -08:00
a5147aa2d9 Add a limited form of re-entrant calls.
This doesn't let you arbitrarily call back into the VM from within
foreign methods. I'm still not sure if that's even a good idea since
God knows what that would mean if you switch fibers while doing that.

But this does allow the very important use case of being able to call
a foreign method from within a call to wrenCall(). In other words,
foreign methods need to always be leaf calls on the call stack, but the
root of that stack can now come from runInterpreter() or wrenCall().

Fix #510.
2019-02-08 17:09:39 -08:00
6ac829947a blog; fix twitter link to munificentbob (thanks @noelfb) 2019-02-07 20:40:44 -08:00
25a1431fa7 docs; add some links to blog post 2019-02-07 13:07:21 -08:00
27a08151e7 blog; clarify weird grammar (sponge) 2019-02-05 19:39:38 -08:00
57a1d809d8 docs; replace wren repo URL to point to organization 2019-02-05 18:41:31 -08:00
7cc7c715c9 docs; list; add + operator documentation 2019-02-05 18:18:38 -08:00
21024bfa13 docs; formatting and tidying
- quick clean up pass on layout and colors for clarity/legibility
- added logo to the sidebar
- fix various css things like a full height page when content is shorter, so the footer doesn't creep away from the bottom
- start using em instead of px for consistency across devices
2019-02-05 18:18:24 -08:00
2d449cd4c9 docs; add blog section with simple rss
- rss is hand written right now
- see the hello wren blog post for reasoning
2019-02-05 18:15:29 -08:00
29cef0bd53 docs; update generator
- copies files in doc/site/static/ to output
- update output formatting to include potential differences in markdown generator (local generate had &amp;gt;, so cover both to remain consistent)
- copy rss xml (could be in static too, but for now...)
2019-02-05 18:14:34 -08:00
201ae2f38a Merge pull request #619 from PrimordialHelios/fix_doc_generation
Fix doc generation script
2019-02-04 17:57:52 -08:00
cabab755f5 Merge pull request #646 from nenofite/patch-1
Fix broken link for error handling
2018-12-19 18:36:30 -03:30
d01905ef80 Fix broken link for error handling 2018-12-19 12:39:52 -08:00
193e0c812c Change size of label on line 449 to avoid incorrect compiler warning about label's maximum length 2018-12-19 09:38:26 -05:00
4ebb99d27f Merge pull request #636 from datatypevoid/readme-update
chore(README): point broken links to wren.io
2018-11-26 16:35:42 -03:30
afebb24e1b chore(README): point broken links to wren.io 2018-11-26 14:33:35 -05:00
00491480d7 Add myself to AUTHORS file 2018-11-06 20:01:05 -05:00
31e179907d Add support for home, end, delete, and ctrl+w 2018-10-31 18:32:34 -04:00
091d2c4313 Fix call to markdown.markdown 2018-10-30 15:06:50 -04:00
a4b034f30b Fixup Handling of \ in REPL to Prevent Crash
When parsing the string token if a `\` was used it would
unconditionally consume the next character. This would led to a token
with an invalid lenght causing a crash when it was printed. This fixes
that by only consuming the token after the `\` if there is one to
consume.

Fixes #603
2018-10-08 07:35:55 +01:00
8929415847 Remove unused prototype. 2018-09-27 15:03:10 +02:00
744147fd94 Flush prompt in animals example. 2018-08-01 07:28:48 -07:00
fefed21158 Rename include guard 2018-07-27 18:57:17 +04:00
fe57c1eea0 Trivial: wren_core.wren.inc needs to be regenerated. 2018-07-25 11:58:50 +02:00
051e224ce6 De-inline wrenHasError.
Move fiber error detection inside a function instead of using inlined
version everywhere.
2018-07-25 11:55:03 +02:00
0ebf4e1277 Add me to AUTHORS. 2018-07-25 11:55:03 +02:00
e47f17567e Fix a couple more casts needed in C++. 2018-07-24 07:27:18 -07:00
03ec25720d Add cast to satisfy the C++ warning gods. 2018-07-24 07:21:41 -07:00
c237a44b88 Add file to test directory so Git creates directory. 2018-07-24 07:15:04 -07:00
f23c82071a Don't allow calling the root fiber.
The VM used to not detect this case. It meant you could get into a
situation where another fiber's caller had completed. Then, when it
tried to resume that fiber, the VM would crash because there was nothing
to resume to.

This is part of thinking through all the cases around re-entrancy. Added
some notes for that too.
2018-07-21 10:02:29 -07:00
5f29a72d65 Remove the special method type for Fn.call(...).
Instead, using regular primitives for each of the call() methods.
Doesn't help performance (alas), but does seem cleaner to me.
2018-07-18 08:20:11 -07:00
ed64a13778 Add new files to api_test target in XCode project. 2018-07-18 07:30:18 -07:00
fe7e56d508 Merge branch 'rfc-0001-smarter-imports' 2018-07-16 07:03:00 -07:00
b2b2b8a4e7 Add note. 2018-07-16 07:02:53 -07:00
1a37d2228d Merge branch 'smarter-imports' 2018-07-16 07:00:08 -07:00
7a42a20b98 Merge branch 'master' into smarter-imports
# Conflicts:
#	src/module/io.c
#	src/vm/wren_vm.c
2018-07-15 21:01:14 -07:00
c367fc3bfc Get logical imports in "wren_modules" working.
There's a lot of changes here and surely some rough edges to iron out.
Also, I need to update the docs. But I want to get closer to landing
this so I can build on it.
2018-07-15 20:09:41 -07:00
e486c56ae9 Merge pull request #546 from mathewmariani/master
Fixed PreBuildEvents on vs2017
2018-07-15 11:08:21 -07:00
09f4beff4a Add trim methods on String:
- trim()
- trim(chars)
- trimEnd()
- trimEnd(chars)
- trimStart()
- trimStart(chars)
2018-07-15 10:48:56 -07:00
ef5f38b48f Turn on auto-deploying docs.
Looks like it's working now. <crosses fingers>
2018-07-14 21:35:33 -07:00
7b42a9d621 More guessing at package names. 2018-07-14 21:00:59 -07:00
361b57fa8d Try to get right package name for smartypants. 2018-07-14 20:51:48 -07:00
3920818b46 Install smartypants on Travis. 2018-07-14 20:44:22 -07:00
c7c69d1291 Check in shared Xcode file. 2018-07-14 12:12:37 -07:00
09be27d3d5 Install pygments on Travis. 2018-07-14 12:12:07 -07:00
7d8292fe90 Fix a Travis dependency. 2018-07-14 11:57:11 -07:00
52f08aec05 Install the custom pygments lexer using python3. 2018-07-14 11:44:53 -07:00
d13cafed71 Fix command. I am an idiot. 2018-07-14 11:17:36 -07:00
db77a9dce4 Disable container-based build so we can use sudo. 2018-07-14 11:05:41 -07:00
5fff693530 Testing fix to doc deploy script. 2018-07-14 10:35:45 -07:00
08b5492362 Turn off doc deploying for now. 2018-07-13 09:15:26 -07:00
c120769977 Make some tweaks to doc deploy script.
- Rename file to match other naming conventions.
- Simplify condition a little.
2018-07-13 08:02:37 -07:00
91b4f53ee5 Merge branch 'auto-deploy-docs-from-travis' of https://github.com/CodogoFreddie/wren into CodogoFreddie-auto-deploy-docs-from-travis 2018-07-13 07:46:46 -07:00
1f93e16fb2 Provide actual soname when building shared lib on Linux.
Fix #572.
2018-07-13 07:19:06 -07:00
646fedab98 Merge pull request #555 from GHvW/GHvW-patch-1
Update file.markdown
2018-07-13 07:02:49 -07:00
a3c2402ca0 Merge branch 'missing_undef' of https://github.com/dressupgeekout/wren into dressupgeekout-missing_undef
# Conflicts:
#	AUTHORS
2018-07-13 07:01:57 -07:00
1029d5feb6 Merge pull request #581 from michal037/michal037
Small improvements
2018-07-13 06:53:34 -07:00
ad9a0e13ac Small improvements
* Safer definitions in io.c
* Easier to understand information in repl.wren
* Added my e-mail; Contributing > Hacking on the VM
2018-07-12 21:50:23 +02:00
5356ccb760 Add myself to the AUTHORS 2018-06-14 00:33:59 -07:00
e7b1bade1a Check that there is space in local variable array
Otherwise it can overflow due to for loop's hidden
variables.

Fix #561
2018-06-13 00:59:55 +03:00
18f74d89c4 Don't forget to #undef FINALIZER 2018-06-05 13:27:45 -07:00
4c9209c1b2 Update file.markdown
minor fix to close() language
2018-06-03 23:06:40 -05:00
9934d17019 Update PreBuildEvent scripts for more conditions 2018-05-21 11:14:23 -04:00
85b2c9a99d added explicity vs2017 argument
To build with VS2017 you need to explicitly add a vs2017 argument
2018-05-21 01:57:50 -04:00
4957fff584 Fixed PreBuildEvents on vs2017 2018-05-21 01:25:38 -04:00
c40bb93d60 Changed shebang to python3 2018-05-16 23:10:44 +02:00
abd1cf37c4 Repaired amalgamation script
The amalgamation script now searches for files in the different
directories of src.

Also the full paths in the amalgamation were replaced by the basename
to produce the same content on all machines.

The makefile now creates the build directory for the amalgamation
if it does not exist.
2018-05-12 09:24:23 +02:00
40c927f440 Make sure we consume all the input when compiling a single expression.
Fix #528.
2018-04-28 16:54:33 -07:00
c4ae0f5c59 Be a little more conservative with some string operations.
This should hopefully fix #531, though in practice the previous code
should have been safe too.
2018-04-28 16:38:09 -07:00
41a56446c6 Refine a few things:
- Fix some doc comments.

- Inline comparing two ObjStrings, since it's only used in one place.
  Also, this avoids a redundant identity check.

- Move the forward declarations of the object types out of
  wren_common.h. Instead, I just added the one needed forward
  declaration of ObjString in wren_utils.h. It's a little inelegant,
  but it feels weird to me to expose all of the object types in
  wren_common.h when they logically belong to wren_value.h and most of
  the types aren't problematic.

- Fix a bug where field symbol tables weren't being marked. If a GC
  happened while compiling a class, field strings got freed.
2018-04-28 12:13:03 -07:00
05e18fa95f Merge branch 'unify_string' of https://github.com/mhermier/wren into mhermier-unify_string 2018-04-28 10:22:42 -07:00
d03ef9e8b0 Move test and tweak code a bit. 2018-04-28 10:08:42 -07:00
4d056b6ec3 Merge branch 'master' of https://github.com/jclc/wren into jclc-master 2018-04-28 10:00:59 -07:00
8fae8e4f1e Don't overflow signature string if there are too many parameters.
Fix #494.
2018-04-27 09:13:40 -07:00
c5ce6fac46 Fix local variable declarations in the REPL.
A statement like:

  for (i in 1..2)

When run in the REPL declares a local variable ("i"), but not inside
a function or method body. This hit a corner case in the compiler
where it didn't have the correct slot indexes set up.

That corner case is because sometimes when you compile a chunk, local
slot zero is pre-allocated -- either to refer to "this" or to hold the
closure for a function so that it doesn't get GCed while running. But
if you're compiling top-level code, that slot isn't allocated. But top
level code for the REPL *should* be, because that gets invoked like a
function.

To simplify things, *every* compiled chunk now pre-allocates slot zero.
That way, there are fewer cases to keep in mind.

Also fixed an issue where a GC during an import could collected the
imported module body's closure.

Fix #456.
2018-04-27 08:20:49 -07:00
64f8bf7ae3 Merge pull request #524 from bjorn/master
Small documentation fixes
2018-04-25 21:09:48 -07:00
4bc3e6f424 Fix LICENSE to match https://choosealicense.com/licenses/mit/.
That way GitHub and other tools correctly recognize the license. There
is no substantive change to the license itself.
2018-04-23 16:48:47 -07:00
e66115c9fc add regression test for #520 2018-04-10 18:53:56 +03:00
c1ed027973 Merge pull request #523 from jaromirmuller/patch-1
documentation: fixed typo, function declares data, body uses file
2018-04-06 23:49:55 -07:00
4034a6d65a crunched BOM skipping 2018-04-07 06:04:32 +03:00
67ed36e79a don't publish docs from PRs, only from merges into master 2018-04-06 20:04:34 +01:00
f7a61df634 only upload docs from the master branch 2018-04-06 19:53:53 +01:00
372cd3e197 Small documentation fixes 2018-04-06 10:34:12 +02:00
4442b37411 fixed typo, function declares data, body uses file 2018-04-05 23:02:30 +02:00
492763205b introduced docs auto publish step to travisci 2018-04-04 20:37:05 +01:00
fe2ca0e89a remove extra line 2018-04-04 05:14:05 +03:00
8858161484 skip UTF-8 BOM 2018-04-04 05:11:59 +03:00
e4901c51d8 Add wrenStringEqualStrLength and wrenStringsEqual. 2018-03-29 09:12:32 +02:00
3c39f7e0af Make symboltable use ObjString.
* Avoid to have 2 different string representation in the code.
* Reduce memory creation in meta API, by reusing created strings.
2018-03-28 18:12:50 +02:00
3e9bf0277b Move Obj* type definition to wren_common.h.
Uniformize structure naming, and should help file includes.
2018-03-28 17:51:34 +02:00
7487eeab49 Merge pull request #515 from kjk/patch-1
fix bindForeignMethod() example
2018-03-26 07:52:26 -07:00
af9fda5e60 Merge branch 'master' of https://github.com/munificent/wren 2018-03-26 07:50:49 -07:00
8c7174d1c6 Merge branch 'fix-assert' of https://github.com/maxdeviant/wren into maxdeviant-fix-assert 2018-03-26 07:50:16 -07:00
8852e5accc Merge pull request #500 from mhermier/critical_fixes
[critical] Fix getNumArguments and wrenBindMethodCode opcodes data.
2018-03-26 07:49:54 -07:00
8210452970 Relative imports!
This is a breaking change because existing imports in user Wren code
that assume the path is relative to the entrypoint file will now likely
fail.

Also, stack trace output and host API calls that take a module string
now need the resolved module string, not the short name that appears in
the import.
2018-03-24 11:10:36 -07:00
5539c59750 Add a minimal path manipulation C module.
This is just for the VM's own internal use, for resolving relative
imports.

Also added a tiny unit test framework for writing tests of low-level
C functionality that isn't exposed directly by the language or VM.
2018-03-24 10:58:07 -07:00
8a71735e0f Expose an API to let the host resolve relative import strings.
This is a breaking API change: wrenInterpret() now takes an additional
parameter for the module name to interpret the code in.
2018-03-23 07:54:09 -07:00
e1f105fd8e Merge pull request #484 from feiss/patch-1
parameter missing in errorFn
2018-03-22 10:31:01 -07:00
bcc31e63a2 add() method is bound as static but bindForeignMethod responds to non-static 2018-03-20 17:13:28 -07:00
c5befa72cf Don't use module import string when loading imported variables.
This is an interim step towards supporting relative imports. Previously,
the IMPORT_VARIABLE instruction had a constant string operand for the
import string of the module to import the variable from. However, with
relative imports, the import string needs to be resolved by the host
all into a canonical import string. At that point, the original import
string in the source is no longer useful.

This changes that to have IMPORT_VARIABLE access the imported ObjModule
directly. It works in two pieces:

1. When a module is compiled, it ends with an END_MODULE instruction.
   That instruction stores the current ObjModule in vm->lastModule.

2. The IMPORT_VARIABLE instruction uses vm->lastModule as the module to
   load the variable from. Since no interesting code can execute between
   when a module body completes and the subsequent IMPORT_VARIABLE
   statements, we know vm->lastModule will be the one we imported.
2018-03-20 06:54:51 -07:00
7612b6bc8c Merge pull request #513 from snocl/replfix
Fix REPL being broken by latest commit
2018-03-19 14:08:04 -07:00
92cb88366b Fix REPL being broken by latest commit. 2018-03-19 21:50:12 +01:00
2c88e19497 Compile imports to closures, not fibers.
This is simpler and marginally faster. We don't need the overhead of
fibers since you can't have long or recursive import chains anyway.

More importantly, this makes the behavior more well-defined when you do
things like yield from an imported module. (Not that you should do that,
but if you do, it shouldn't do weird things.)
2018-03-17 09:33:33 -07:00
43cf652003 Simplify wrenCompileSource(). 2018-03-17 09:13:51 -07:00
bd3897278c Use newer GYP. 2018-03-14 08:06:06 -07:00
a95077cd9f Don't build the libuv tests on Linux. 2018-03-14 07:27:29 -07:00
011e8fab96 Fix off-by-one error in arg checking. 2018-03-14 07:27:29 -07:00
f866ee7003 Vendor GYP and libuv into the repo.
Instead of dynamically downloading these as needed during a build, this
checks in those two dependencies directly into the Wren repo. That's a
little lame because users of Wren who aren't building the CLI don't
actually need them, but they aren't too big, so it's not a huge deal.

It makes builds (particularly on Travis) more reliable, because they
don't have to pull down additional content over the network.
2018-03-14 07:27:29 -07:00
3ce9ecfc65 Merge pull request #506 from adsr/patch-readme
Fix embedding link in README
2018-02-20 14:23:39 -08:00
0ad0af0e1f Fix embedding link in README 2018-02-16 23:18:01 -05:00
89ae3f92ec Add @maxdeviant to AUTHORS 2018-01-18 00:48:02 -05:00
e493e6679b Fix variable in assert
Fixes #493
2018-01-18 00:46:58 -05:00
a2cf3c34ad Fix getNumArguments and wrenBindMethodCode opcodes data.
Some instructions data in these 2 functions are out of syncs, and can try to
patch the code in bad manner.
2018-01-11 12:20:35 +01:00
d1a772e820 parameter missing in errorFn 2017-11-14 16:56:16 +01:00
c4d08bcb81 Fix missing return error. 2017-10-31 22:07:07 -07:00
7b326d804b Merge pull request #475 from KyleCharters/master
Correct method examples in Range docs
2017-10-31 16:24:58 -07:00
8d37b20783 Compile imports to actual instructions instead of core library calls. 2017-10-31 15:58:59 -07:00
e9ac543853 Merge pull request #480 from aliou/ad-fix-embedding-path
Fix embedding path in getting started
2017-10-31 14:04:37 -07:00
b45cab28d1 Fix embedding path in getting started. 2017-10-31 21:47:51 +01:00
9f79dfcdac Proposal for smarter imports. 2017-10-31 07:49:40 -07:00
1be76bb478 Correct method examples in Range docs 2017-10-23 13:48:43 -04:00
88ff8f90ef Merge pull request #474 from minirop/typo-in-doc
missing language specifier and variable declaration
2017-10-19 22:12:25 -07:00
1661f5368f Allow passing a value when first starting a fiber.
If the function the fiber is created from takes a parameter, the value
passed to the first call() or transfer() gets bound to that parameter.

Also, this now correctly handles fibers with functions that take
parameters. It used to leave the stack in a busted state. Now, it's a
runtime error to create a fiber with a function that takes any more
than one parameter.
2017-10-19 20:45:13 -07:00
e043a95e4e Edit docs. Docs docs docs. 2017-10-19 19:52:05 -07:00
6444a5f5db missing language specifier and variable declaration 2017-10-19 21:17:40 +02:00
f8076c2c1c Revise embedding docs. 2017-10-19 07:02:18 -07:00
e2e16dc4a9 Finish off the rest of the embedding docs.
A first draft of them, at least. They probably need some editing.
Remove the "Application Lifecycle" page for now. I do intend to add
some docs about how fibers interact with the host app, but I can do
that later.
2017-10-17 08:26:06 -07:00
9661a5b999 Fix a couple of bugs in the REPL.
- Not sure what was going on, but fix #456. It makes more sense for the
  REPL to invoke the compiled code as a fiber than a function anyway.

- Flush stdout before reading from stdin since System.print() no longer
  does that automatically.
2017-10-13 07:58:57 -07:00
97b2e1c818 Add web server to doc generator. 2017-10-12 06:38:34 -07:00
91853a7fa9 Fix comment on foreign allocate function.
Fix #453.
2017-10-12 06:37:49 -07:00
2021e086bf Don't automatically flush on every System.write().
It's handy because it ensures writes are flushed to the terminal before
any calls to read from stdin, but it's also gratuitously slow.

Instead, added a Stdout class with an explicit flush() method that can
be called by the user.

Fix #445.
2017-10-09 07:16:05 -07:00
c4b0f83cb3 "ClassCompiler" -> "ClassInfo".
Fix #469.
2017-10-09 06:47:42 -07:00
973b43eb7f Simplify statement compiler code a little. 2017-10-08 10:47:31 -07:00
c7087652ee Add regression tests from wren-fuzz.
These are all non-crashing now, so it looks like the underlying issue
is fixed. (It was probably #429.)

Fix #442.
2017-10-08 10:15:15 -07:00
c38d97b973 Properly detect a missing "}" in a block.
Fix #429.
2017-10-08 10:03:42 -07:00
18449102e8 Parenthesize macro arguments in ALLOCATE_FLEX and ALLOCATE_ARRAY.
Fix #457.
2017-10-08 09:45:06 -07:00
fec54d5021 Avoid undefined pointer difference behavior.
Fix #458.
2017-10-08 09:42:35 -07:00
eba0b97aa2 Merge pull request #472 from KyleCharters/master
Add Num .round
2017-10-06 07:51:55 -07:00
8963a870ff Abort all fibers along a call chain when one aborts. 2017-10-06 07:39:00 -07:00
0f19984017 Merge branch '201703_bubble_exceptions' of https://github.com/JonesAndrew/wren into JonesAndrew-201703_bubble_exceptions 2017-10-06 06:58:27 -07:00
61f5e56e93 Merge pull request #460 from skybrian/fixcomment
Remove obsolete comment
2017-10-05 06:51:48 -07:00
2bb531d860 Tweak Fiber.error docs. 2017-10-05 06:50:45 -07:00
84f2252c68 Add Num .round 2017-09-20 20:27:02 -04:00
16bab57056 fix #468 2017-09-16 21:38:35 -07:00
a7418d64e6 Remove obsolete comment 2017-05-29 12:02:50 -07:00
d390e16a99 Kill duplicate test.
Fix #438.
2017-04-08 12:53:33 -07:00
7aa3fc1ce7 Merge pull request #435 from Nelarius/vs
Modernize the Visual Studio solution
2017-04-08 12:48:47 -07:00
6ccdc0cf19 Merge pull request #443 from nathanhourt/nathanhourt-typo-fixes
One more typo fix
2017-04-08 12:40:31 -07:00
889cae5ff1 Free imported module source strings after compiling them.
Fix #430.
2017-04-08 12:38:36 -07:00
e9896544c8 Typo fix: is only be -> will only be 2017-04-04 15:29:53 -05:00
a6424e94b1 Merge pull request #440 from nathanhourt/patch-1
Fix typos in documentation
2017-04-04 07:07:57 -07:00
7d631276cb Catch errors in the first try() 2017-03-30 21:57:29 -04:00
a01b8d12bd Typo fix: askes -> asks 2017-03-30 20:20:02 -05:00
8f14bfb4b8 Fix typo in Num documentation
The documentation for the bitwise or operator described the behavior of the bitwise and operator.
2017-03-30 17:45:12 -05:00
21b4032fae Remove Visual Studio 2013 solution 2017-03-28 22:06:03 +03:00
a32f54126b Add Visual Studio 2017 solution 2017-03-28 22:05:58 +03:00
417ed7aeed Add wren.hpp. 2017-03-25 10:05:33 -07:00
b32c445f96 Show constructing "blondie" instance in functions doc.
Fix #432.
2017-03-23 21:22:07 -07:00
44a95e65c6 Set the token type after an invalid character error.
Otherwise, it still has the previous token's type. This can cause it
to get stuck in a loop in some places in the compiler.

Fix #428.
2017-03-23 21:19:20 -07:00
02bcefcbe4 Add test for user data.
Also moved the VM parameter in the error callback to be first like it
is in other callbacks.
2017-03-22 07:26:19 -07:00
907db83e1c Merge branch 'user-data' of https://github.com/foobit/wren into foobit-user-data 2017-03-22 07:12:26 -07:00
8d313be3ce Make it an error to skip or take a negative count. 2017-03-15 07:22:44 -07:00
9f93119377 Merge branch 'take_and_skip' of https://github.com/bjorn/wren into bjorn-take_and_skip 2017-03-15 07:15:00 -07:00
c84b8109a4 Tweak comments. 2017-03-15 07:10:23 -07:00
627e9edc23 Merge branch 'fix362' of https://github.com/bytemaster/wren into bytemaster-fix362 2017-03-15 07:09:31 -07:00
9efb18bae2 Typo: "seperator" -> "separator". 2017-03-15 07:06:07 -07:00
9fcdf3dc0a Merge branch '201703_split_replace' of https://github.com/JonesAndrew/wren into JonesAndrew-201703_split_replace 2017-03-15 07:04:56 -07:00
32aa43d1df Split and replace in wren. 2017-03-07 21:15:06 -05:00
860e596b8a Test wrenSetSlotNull(). 2017-03-03 07:57:50 -08:00
f14d13a6bf Mention libm in docs. 2017-03-02 07:17:03 -08:00
22ae382ac5 Proofread configuring the VM. 2017-02-12 14:58:16 -08:00
18f8f48bb6 Docs for configuring the VM. 2017-02-12 11:04:38 -08:00
996a607026 More work on embedding docs.
- Revise the earlier pages and tighten them up some.
- Write the docs for foreign methods.
2017-02-12 10:14:37 -08:00
f5d9443d0a Added Sequence.take and Sequence.skip
These lazy iterator producing methods are useful when working with
arbitrary sequences and you need to skip or take some number of elements
at the start.
2017-02-10 21:43:59 +01:00
4fe3ad3f8b Docs for Num.log and Num.pow(). 2017-01-20 07:24:46 -08:00
3faec25c4c Merge branch 'num-features' of https://github.com/underscorediscovery/wren into underscorediscovery-num-features 2017-01-20 07:20:55 -08:00
cdce553f5a fixed new api issue in cli 2017-01-19 22:58:52 -05:00
dc2f2d9e41 support for user defined state data associated with the WrenVM object 2017-01-19 22:54:25 -05:00
c6eb0be990 Remove the negative number tests for bitwise not.
They were inadvertently relying on undefined behavior in C and we get
different results on some compilers.

Until we decide how we want the operation to behave, for now, just
leave it unspecified.
2017-01-12 22:17:33 -08:00
d301fefac2 Add a comment explaining the ci targets. 2017-01-12 21:55:35 -08:00
5ddd783ff5 Use strtoll() for hex literals to handle 64-bit ones even on 32-bit. 2017-01-12 21:53:21 -08:00
0cc00c41c0 Simplify test runner output a little. 2017-01-12 21:38:06 -08:00
33a73ef1d5 Avoid using fpclassify() to detect infinity and nan.
It's not supported in C++98. Instead, use isnan() and isinf(), which
seem to work? If nothing else, they are used elsewhere in Wren, so if
we're not going to use them here, we should fix the other places too.
2017-01-12 21:34:11 -08:00
e8dfb1bf10 Don't test bitwise operations on operands that don't fit in u32.
The current behavior is undefined in C when converting the double to a
u32, so the tests fail on some compilers. For now, I'm just removing
those parts of the tests because I'm not sure what I want the behavior
to be. Modulo? Truncate? Runtime error?
2017-01-12 21:32:50 -08:00
252265c80b Merge branch 'travis-build-configurations' of https://github.com/iwillspeak/wren into iwillspeak-travis-build-configurations 2017-01-12 20:19:52 -08:00
f03647cbf0 Merge branch 'gc-32-bugfix' of https://github.com/iwillspeak/wren into iwillspeak-gc-32-bugfix 2017-01-12 18:53:14 -08:00
479ca7ec6d Improve explanation of single-expression blocks.
Fix #390.
2017-01-12 11:05:03 -08:00
0a2bc4a8a7 More copy editing. 2017-01-12 07:11:01 -08:00
fc6a7e5d18 Tweak some docs a little. 2017-01-12 07:10:19 -08:00
a9f49b89a4 Fix Bug in GC Reallocation Calculation
Updates the way we calculate thenext GC to make sure that we're not
already past the threshold. This was causing endless garbage collections
on 32 bit builds in `test/language/deeply_nested_gc.wren`.
2016-12-29 17:53:27 +00:00
04a7c9b5c8 Improve Travis Build & Test Coverage
Build Wren for more targets, and run the test suite on both 32 and 64
bit builds.

 * Update the build config to test both with and without NAN_TAGGING
   defined.

 * Updatest `util/test.py` to take the executable suffix as a
   parameter. This allows the makefile to control which binaries will be
   tested.

   Adds a new target to the makefile to be run by travis, this runs the
   test suite against all of the configurations it builds.

 * Gcc on some 32 bit platforms was complaining about numeric overflows
   when -INFINITY was used. Update the logic for converting a double to
   a string to not explicitly check against the literal values.

 * Make CI builds run the tests on both 64 _and_ 32 bit builds.

 * If I limit the number of CPUs on my MBP I can get some of the tests
   to time out, I'm imagining that the specs of the Travis Macs means
   that the same is happening there too. Updated the test script to
   allow an extra few seconds for the test to complete successfully
   before killing it.

 * Due to slight differences in accuracy in some computations tests were
   failing on 32 bit builds. Stop comparing things quite as exactly in
   the cases where it is causing issues.

   For some reason 12.34 was refusing to compare equal to itself. Bad
   show 12.34 :-/. I've also updated the test so it doesn't leak handles
   even if the assertions fail.

 * Double-cast from `double` to `uint32_t` to prevent undefined
   behaviour on overflow of basic integers. This should hopefully
   prevent 32 bit test failures on Linux.

 * Move to a version of LibUV with a fix for the 32 bit build error on
   Travis.
2016-12-29 17:52:38 +00:00
cefb50f944 Fix Bug in GC Reallocation Calculation
Updates the way we calculate thenext GC to make sure that we're not
already past the threshold. This was causing endless garbage collections
on 32 bit builds in `test/language/deeply_nested_gc.wren`.
2016-12-29 17:52:38 +00:00
b9f53f71fb Reset API stack a fiber is aborted from wrenCall(). 2016-11-01 08:40:16 -07:00
51e50e6cc4 Free libuv data before resuming after an error. 2016-11-01 08:39:30 -07:00
a2868913c5 Add .log to Num 2016-10-31 17:22:32 -02:30
955e92761d Add .pow(_) to Num 2016-10-31 17:22:13 -02:30
2650794340 Update AUTHORS 2016-10-31 17:21:55 -02:30
a3aaad83ea Fix bug #362 calling Foreign methods
I assume that invoking callForeign that the apiStack should be restored
to it's original value, rather than reset to NULL.
2016-10-26 13:51:29 -04:00
57f34fab95 Fix assignment test. 2016-10-08 10:51:19 -07:00
e7cabbb5e4 Reset the API stack after a foreign constructor returns. 2016-08-28 08:23:27 -07:00
d331cbf43d Ensure macro parameter is parenthesized. 2016-08-27 21:44:39 -07:00
b59eeaf2d1 Support "--version" in the CLI to print the version. 2016-08-27 17:38:14 -07:00
09ef904d32 Use DBL_MIN instead of DBL_EPSILON for Num.smallest. 2016-08-27 17:25:32 -07:00
b8b0f36521 Merge branch 'num-properties' of https://github.com/Nelarius/wren into Nelarius-num-properties 2016-08-27 17:18:21 -07:00
0a4f1b370c Revise embedding docs a bit. 2016-08-26 07:11:50 -07:00
a8cf1c9d71 Const correct a couple of functions.
Thanks, Michel!
2016-08-16 07:43:54 -07:00
febb1b0437 Use a map to check for constant collisions.
Doing an O(n) lookup was too slow.
2016-08-16 07:43:34 -07:00
08eceb7382 Check for duplicates when adding constants.
This saves a little memory, but, more importantly, allows us to support
functions with large numbers of repeated constants.
2016-08-16 07:24:04 -07:00
6ee1743429 Update Num class doc 2016-08-04 21:17:45 +03:00
50fa77356e Add largest, smallest properties to Num class 2016-08-04 21:15:55 +03:00
500c7b0426 Add List constructors to docs. 2016-08-04 06:28:41 -07:00
3666eae013 Tweak new list constructors.
- Remove List.new(_). I was convinced by the issue discussion that
  using it is probably a bad idea. We don't want to encourage more nulls
  in the world than there are already are. So let's see if we can live
  without it and just have List.filled(). That way users think about
  what they're creating a list *of*.
- Added some more tests.
- Correctly handle being given a negative size.
2016-08-03 22:42:31 -07:00
3de394e8a1 Merge branch 'new-list-constructors' of https://github.com/mooxen/wren into mooxen-new-list-constructors 2016-08-03 22:34:08 -07:00
6845328661 Tweak String.indexOf(_,_) a bit.
- Simplify the arithmetic a little in wrenStringFind().
- Allow the start to be negative.
- Even more tests.
- Docs.
2016-08-03 22:19:34 -07:00
e5dce527ac Merge branch 'string-indexof-startindex' of https://github.com/underscorediscovery/wren into underscorediscovery-string-indexof-startindex 2016-08-03 21:51:46 -07:00
83bb043327 Merge branch 'forgotten-bytecode' of https://github.com/minirop/wren into minirop-forgotten-bytecode 2016-08-03 21:45:09 -07:00
60a0353a27 Tweak list C API:
- Rename "size" -> "count" to be consistent with other usage.
- Remove "Slot" from the function names, since that's implicit.
- Make the getListElement() just move the element to another slot. This
  matches other APIs where we distinguish accessing a value and
  converting it to some specific type.
2016-07-28 08:06:36 -07:00
b8675e2d91 Merge branch 'get_slot_list_values' of https://github.com/dradtke/wren into dradtke-get_slot_list_values 2016-07-28 07:53:19 -07:00
244faa5700 renamed filler constructor in list to match issue 2016-07-17 12:24:19 +10:00
45d9794476 added extra arguments to list constructors for default size/ value 2016-07-17 12:01:50 +10:00
1f84a10f44 Implement String indexOf(needle, startIndex) 2016-07-14 01:23:01 -02:30
193d102952 missed bytecode name refactoring 2016-07-12 22:59:02 +02:00
d192a9a02d Add doc mention of new Num properties 2016-07-10 23:26:23 +03:00
817a5650fe Add highest, lowest, epsilon properties to Num class 2016-07-10 22:54:34 +03:00
5418e4f8f3 Add MapEntry to core to let maps be directly iterated.
Fix #372.
2016-07-06 07:17:07 -07:00
fcfb053dc1 Merge branch 'verbose-make' of https://github.com/bfontaine/wren into bfontaine-verbose-make 2016-07-06 06:51:12 -07:00
d7524898dc Merge pull request #376 from paulsapps/master
Fix build, return false instead of NULL
2016-07-06 06:46:35 -07:00
5468d6d033 Fix build, return false instead of NULL 2016-07-02 12:40:00 +01:00
b41a0f7c07 Allow builds to be verbose
It’s impossible to disable @s in Makefiles to get a verbose build.
Adding them conditionally using a variable lets users choose if they
want a verbose build by using the VERBOSE variable.

Normal build:

    make

Verbose build:

    make VERBOSE=1
2016-07-02 11:21:47 +02:00
0837917307 Use a hash function for numbers that doesn't tend to zero low bits.
Just pulling the raw words out of a double means integers often map to
hashes with zero low bits. Then when we map that to an entry in the map,
all small integers end up in entry zero, leading to tons of collisions.

This mixes the bits around some like Java does to fix that.

Makes the map_numeric benchmark about 18% faster on my machine. On a
contrived benchmark that just uses keys up to 1000, this makes it 52x
(yes, *times) faster.
2016-06-27 08:13:45 -07:00
4e94195f26 Properly handle tombstones when inserting items into a map.
Previously, you could get into a state where a key was present in the
map, but after a tombstone in the probe sequence. If they key was added
again, it stopped at the first tombstone and added it there, resulting
in the key being in the map multiple times.

Fix #373.
2016-06-27 07:14:52 -07:00
bc29f43bbd Allow hosts with no module loader to still load optional modules.
If the host doesn't set a loadModuleFn at all in the config, it should
still be able to load random, meta, etc.
2016-06-27 07:08:08 -07:00
9c3163d533 Only reset the TTY if we created the stdin stream. 2016-06-09 19:16:41 -07:00
6ece314863 Reset the TTY before closing the underlying stdin stream. 2016-06-09 19:16:41 -07:00
33cf8f7b31 Fall back to a simpler REPL if it seems like ANSI escapes won't work. 2016-06-09 19:16:41 -07:00
f47e128978 Tab-completion in REPL based on module variables. 2016-06-09 19:16:41 -07:00
98514188b0 More shortcuts:
- Ctrl-B: Move cursor left.
- Ctrl-F: Move cursor right.
- Ctrl-K: Delete everything after cursor.
- Ctrl-L: Clear screen.
- Ctrl-U: Clear line.
- Ctrl-N: Next in history.
- Ctrl-P: Previous in history.
2016-06-09 19:16:41 -07:00
2ba264bc29 Add history to REPL. 2016-06-09 19:16:41 -07:00
bc7f1de758 Execute input in the REPL.
Guesses whether the input is an expression or statement and handles it
appropriately. Finally, after over a year, the Wren REPL automatically
prints "3" if you type in "1 + 2". \o/
2016-06-09 19:16:41 -07:00
689cd42269 Start writing the REPL in Wren.
It doesn't actually execute code yet, but it:

- Supports left and right arrow keys for moving the cursor.
- Ctrl-C, Ctrl-D, Ctrl-A, and Ctrl-E for navigating.
- Syntax highlights the line (!).

The next step is to do a rough parse so that we can tell if the line
is an expression, statement, or needs more input. That will tell us
whether we need to interpret it at the top level (statement) and not
worry about a result, evaluate it as an expression and print the result,
or read more lines.
2016-06-09 19:16:41 -07:00
59e9eb127e Add wrenAbortFiber().
Thanks, @underscorediscovery!
2016-06-09 19:14:21 -07:00
8657a42c21 Merge pull request #366 from underscorediscovery/patch-2
fix range docs to introduction
2016-05-27 07:23:41 -07:00
78deace9a4 Merge pull request #365 from underscorediscovery/patch-1
docs; iterator protocol link relative to core/ not modules/
2016-05-27 07:23:07 -07:00
2b0878ec6d Platform.isWindows. 2016-05-21 12:53:21 -07:00
6002cc4d65 Add Stdin.isTerminal. 2016-05-21 12:51:11 -07:00
06731f66d4 Add a Platform class.
- Rename the "process" module to "os".
- Add Platform to it.
- Static "name" method.
- Static "isPosix" method.
- Docs and tests!
2016-05-21 12:44:17 -07:00
5a8cacfbe4 fix range docs to introduction
Is that the link that was intended?
2016-05-21 00:24:41 -02:30
b57fd6431a iterator protocol relative to core/ not modules/ 2016-05-21 00:21:47 -02:30
cf258b9074 Add support for retrieving list values from a slot
This PR adds support for retrieving list information from a slot. Rather
than take the whole list, two different methods are provided for
retrieving a) the length of the list, and b) a specific value in the
list.
2016-05-19 14:11:48 -05:00
568 changed files with 90545 additions and 8418 deletions

61
.github/workflows/.githubCI.yml vendored Normal file
View File

@ -0,0 +1,61 @@
name: WrenCI
on:
workflow_dispatch:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: build
run: ./.travis.sh
shell: bash
working-directory: ./
- uses: actions/upload-artifact@v2
with:
name: wren-linux
path: |
bin/*
lib/*
mac:
runs-on: macos-latest
env:
WREN_TARGET_MAC: 1
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: build
run: ./.travis.sh
shell: bash
working-directory: ./
- uses: actions/upload-artifact@v2
with:
name: wren-mac
path: |
bin/*
lib/*
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: msbuild
uses: microsoft/setup-msbuild@v1.1
- name: build
working-directory: ./projects/vs2019/
run: msbuild ./wren.sln /property:Configuration=Release /property:Platform=64bit
- uses: actions/upload-artifact@v2
with:
name: wren-windows
path: |
bin/*
lib/*

33
.gitignore vendored
View File

@ -1,29 +1,29 @@
# Build outputs.
# Build outputs
/bin
/lib
/wren
# Intermediate files.
# Intermediate files
*.obj
Debug/
Release/
/build
/deps
/.sass-cache
*.pyc
# I leave a temporary Wren script at the top level so that I can quickly test
# stuff.
# I leave a temporary Wren script at the top
# level so that I can quickly test stuff.
/scratch.wren
# The baseline file is machine-specific, so doesn't get checked in.
# The baseline file is machine-specific
/test/benchmark/baseline.txt
# XCode user-specific stuff.
# VSCode project files.
.vscode
# XCode user-specific stuff
xcuserdata/
# Allow Visual Studio project files.
!wren/
!wren_lib/
# Visual Studio cache files.
# Visual Studio cache files
.vs/
ipch/
*.aps
*.ncb
@ -31,8 +31,11 @@ ipch/
*.sdf
*.cachefile
# Visual Studio User-specific files.
# Visual Studio User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# macOS
.DS_Store

25
.travis.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/bash
set -e
# This build script only builds mac or linux right now, for CI.
WREN_WD="projects/make"
if [ -n "$WREN_TARGET_MAC" ]; then
WREN_WD="projects/make.mac"
fi
WREN_PY=${WREN_PY_BINARY:-python3}
echo "using working directory '$WREN_WD' ..."
echo "using python binary '$WREN_PY' ..."
make -C $WREN_WD config=debug_64bit-no-nan-tagging
$WREN_PY ./util/test.py --suffix=_d
make -C $WREN_WD config=debug_64bit
$WREN_PY ./util/test.py --suffix=_d
make -C $WREN_WD config=release_64bit-no-nan-tagging
$WREN_PY ./util/test.py
make -C $WREN_WD config=release_64bit
$WREN_PY ./util/test.py

View File

@ -1,13 +1,42 @@
language: c
compiler:
- gcc
- clang
# 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.
addons:
apt:
packages:
- gcc-multilib
- g++-multilib
sudo: false # Enable container-based builds.
script: make all && make test
language: c
# https://docs.travis-ci.com/user/languages/c/#gcc-on-macos
# On mac, gcc is aliased to clang, so we only have one row
# in build the matrix, not two like on linux
compiler:
- clang
- gcc
# Automatically build and deploy docs.
jobs:
include:
- os: linux
- os: osx
env: WREN_TARGET_MAC=1
- stage: deploy
script: ./util/deploy_docs_from_travis.sh
# 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.
addons:
apt:
packages:
- gcc-multilib
- g++-multilib
# These are needed for building and deploying the docs.
- python3-markdown
- python3-pygments
- python3-setuptools
- ruby-sass
# Can't do container-based builds for now because installing the custom
# Pygments lexer to generate the docs requires sudo. :( If that changes,
# uncomment the next line and delete the "sudo" and "dist" lines.
# sudo: false # Enable container-based builds.
sudo: required
dist: trusty
script: ./.travis.sh

14
AUTHORS
View File

@ -16,3 +16,17 @@ Evan Hahn <me@evanhahn.com>
Starbeamrainbowlabs <contact@starbeamrainbowlabs.com>
Alexander Roper <minirop@gmail.com>
Will Speak <will@willspeak.me>
Damien Radtke <damienradtke@gmail.com>
Max Ferguson <maxxferguson@gmail.com>
Sven Bergström <sven@underscorediscovery.com>
Kyle Charters <kylewcharters@gmail.com>
Marshall Bowers <elliott.codes@gmail.com>
Michal Kozakiewicz <michalkozakiewicz3@gmail.com>
Charlotte Koch <cfkoch@edgebsd.org>
Michel Hermier <michel.hermier@gmail.com>
Taylor Hoff <primdevs@gmail.com>
ruby0x1 <ruby0x1@pm.me>
Kolja Kube <code@koljaku.be>
Alexander Klingenbeck <alexander.klingenbeck@gmx.de>
Aviv Beeri <avbeeri@gmail.com>

View File

@ -1,3 +1,128 @@
## 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,
streamlining the process for new users and making embedding easier.
This is a stepping stone for working on language features and improving the VM,
hacking on the docs and the VM is simpler than ever!
Builds now work out of the box on all primary platforms.
Previously there was issues on Windows and other platforms due to unix-ey workflows being the default.
All the python scripts have also been fixed and updated (to python 3), and work consistently
across all platforms out of the box too (including the tests, benchmarks, metrics etc).
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 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.
### Language/VM
- CLI moved to own repo
- Use premake for project generation, see projects/
- Fix builds across platforms. "Just works" on all primary platforms.
- Fix amalgamated script generator and amalgamated build
- Fix unicode parsing and other issues in all python scripts
- All python scripts are python3 now, and run on all platforms correctly
- Test runner isolated and unified for VM tests
- Remove SASS and Pygments requirements from docs, just python now
- Updated docs to clarify VM/CLI split
- Added Try page for running wren code in the docs
## 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...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.
Most noteworthy is that 'relative imports' are a slightly breaking change,
but help pave the way forward toward a consistency for modules.
### Language/VM
- `import` was made smarter, differentiating relative from logical
- `Fiber` can now accept a value from the first `call`/`transfer`
- Added `String.trim`, `String.trimEnd`, `String.trimStart` variants
- Added `String.split`, `String.replace`, `String.fromByte`
- Added `String.indexOf(needle, startIndex)`
- Added `Sequence.take` and `Sequence.skip`
- Added `List.filled(count, value)`
- Added `Num.pow`, `Num.log`, `Num.round`
- Added `Num.largest`, `Num.smallest`
- Added `Map` iteration (`MapEntry`)
#### C API
- Added `wren.hpp` for use in c++
- Added void* user data to `WrenVM`
- Allow hosts with no module loader to still load optional modules.
- Added `wrenAbortFiber`
### CLI
Please note that beyond 0.2.0 the CLI will have it's own changelog.
This list is not exhaustive. For a fuller history see the commit log above.
- Add path module
- Add `--version`
- Add REPL written in Wren
- Add Stdin.isTerminal
- Added Platform class
- Rename `process` module to `os`
## 0.1.0
First declared version. Everything is new!

24
LICENSE
View File

@ -1,23 +1,21 @@
Wren uses the MIT License:
MIT License
Copyright (c) 2013-2016 Robert Nystrom
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 in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. (As clarification, there is no
requirement that the copyright notice and permission be included in binary
distributions of the Software.)
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,77 +0,0 @@
# Top-level Makefile. This has targets for various utility things. To actually
# compile Wren itself, it invokes util/wren.mk for the various configurations
# that Wren can be built with.
# Executables are built to bin/. Libraries are built to lib/.
# A normal, optimized release build for the current CPU architecture.
release:
@ $(MAKE) -f util/wren.mk
@ cp bin/wren wren # For convenience, copy the interpreter to the top level.
# A debug build for the current architecture.
debug:
@ $(MAKE) -f util/wren.mk MODE=debug
# A release build of just the VM, both shared and static libraries.
vm:
@ $(MAKE) -f util/wren.mk vm
# A release build of the shared library for the VM.
shared:
@ $(MAKE) -f util/wren.mk shared
# A release build of the shared library for the VM.
static:
@ $(MAKE) -f util/wren.mk static
# Build all configurations.
all: debug release
@ $(MAKE) -f util/wren.mk LANG=cpp
@ $(MAKE) -f util/wren.mk MODE=debug LANG=cpp
@ $(MAKE) -f util/wren.mk ARCH=32
@ $(MAKE) -f util/wren.mk LANG=cpp ARCH=32
@ $(MAKE) -f util/wren.mk MODE=debug ARCH=32
@ $(MAKE) -f util/wren.mk MODE=debug LANG=cpp ARCH=32
@ $(MAKE) -f util/wren.mk ARCH=64
@ $(MAKE) -f util/wren.mk LANG=cpp ARCH=64
@ $(MAKE) -f util/wren.mk MODE=debug ARCH=64
@ $(MAKE) -f util/wren.mk MODE=debug LANG=cpp ARCH=64
# Remove all build outputs and intermediate files. Does not remove downloaded
# dependencies. Use cleanall for that.
clean:
@ rm -rf bin
@ rm -rf build
@ rm -rf lib
# Remove all build outputs, intermediate files, and downloaded dependencies.
cleanall: clean
@ rm -rf deps
# Run the tests against the debug build of Wren.
test: debug
@ $(MAKE) -f util/wren.mk MODE=debug test
@ ./util/test.py $(suite)
benchmark: release
@ $(MAKE) -f util/wren.mk test
@ ./util/benchmark.py -l wren $(suite)
# Generate the Wren site.
docs:
@ ./util/generate_docs.py
# Continuously generate the Wren site.
watchdocs:
@ ./util/generate_docs.py --watch
# Build the docs and copy them to a local "gh-pages" directory.
gh-pages: docs
@ cp -r build/docs/. build/gh-pages
# Build amalgamation of all Wren library files.
amalgamation: src/include/wren.h src/vm/*.h src/vm/*.c
./util/generate_amalgamation.py > build/wren.c
.PHONY: all amalgamation builtin clean debug docs gh-pages release test vm watchdocs

View File

@ -44,15 +44,15 @@ If you like the sound of this, [let's get started][started]. You can even try
it [in your browser][browser]! Excited? Well, come on and [get
involved][contribute]!
[![Build Status](https://travis-ci.org/munificent/wren.svg)](https://travis-ci.org/munificent/wren)
[![Build Status](https://travis-ci.org/wren-lang/wren.svg?branch=main)](https://travis-ci.org/wren-lang/wren)
[syntax]: http://munificent.github.io/wren/syntax.html
[src]: https://github.com/munificent/wren/tree/master/src
[nan]: https://github.com/munificent/wren/blob/46c1ba92492e9257aba6418403161072d640cb29/src/wren_value.h#L378-L433
[perf]: http://munificent.github.io/wren/performance.html
[classes]: http://munificent.github.io/wren/classes.html
[fibers]: http://munificent.github.io/wren/concurrency.html
[embedding]: http://munificent.github.io/wren/embedding-api.html
[started]: http://munificent.github.io/wren/getting-started.html
[syntax]: http://wren.io/syntax.html
[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
[fibers]: http://wren.io/concurrency.html
[embedding]: http://wren.io/embedding/
[started]: http://wren.io/getting-started.html
[browser]: http://ppvk.github.io/wren-nest/
[contribute]: http://munificent.github.io/wren/contributing.html
[contribute]: http://wren.io/contributing.html

72
doc/notes/debugger.md Normal file
View File

@ -0,0 +1,72 @@
# The debugger is work in progress
This branch is very experimental and not finished.
There's a lot of exploratory code that isn't well defined or cleaned up.
This is expected.
We welcome contribution to make it more complete.
Please open a discussion to discuss major changes before doing them.
https://github.com/wren-lang/wren/issues/425
The original implementation was done by @KeyMaster- ([twitter](https://twitter.com/keymaster_)) as a learning
experience in [luxe](https://luxeengine.com), so thanks to them!
# Testing the debugger
- enable `#define WREN_DEBUGGER 1` in include/wren.h
- enable `#define WREN_DEBUGGER 1` in wren_common.h
- call `wrenDebuggerPollConfigCmds(vm)` in e.g a main loop
- implement the config stuff needed
```c
const char* get_module_path(WrenVM* vm, const char* module, const char* root)
{
//return a path on disk for vscode to open when stepping into code etc
}
/// ....
//config
config.modulePathFn = get_module_path;
config.debuggerPort = "8089";
config.enableDebugger = true;
```
# Accessing the debugger
The debugger operates a simple protocol over a network socket.
You can connect to it with telnet for example and control it.
The vscode extension below integrates with it for a bigger example.
# The (very wip) vscode extension
Debugging shown in this video in vscode was made possible using this extension:
https://github.com/wren-lang/wren-vscode
The extension is also very rough and experimental.
# old original todo notes from @KeyMaster-
- [ ] Patch method field indices in ObjModule->fieldSlots
- [x] Make it run on windows (specifically socket.h and related)
- [ ] Conform to wren code style
- [ ] Better naming of things and protocol messages
- [ ] Local variables which are only passed to other functions aren't being tracked
- [ ] Test behaviour on fiber switches
- [ ] Expand upvalue valid range to the whole function they are defined in
- [ ] Static variables not used in a function are not visible to the debugger
- [ ] patch bytecode for breakpoints and trap instead of checking constantly in loop
**Smaller details**
- [ ] Remove breakpoints maximum? would add dynamic allocation though.
- [ ] Remove max length of module names in breakpoint struct
- [ ] Stack excludes foreign calls and core module calls, decide if this should be done. (affects getStackIdxFrame)
- [ ] Stack send, uses `|` to delineate but module string could contain `|`. Escape or pass module name string length.
- [ ] Check memory management of modulePathFn, requires free on caller side
- [ ] loadModuleFn gets called again by the debugger, update corresponding comment. (Or cache resuls?)
- [ ] Max length of scanned var name in debuggerSendVar does not use MAX_VARIABLE_NAME macro (to insert it into the string we require more macros though)
- [ ] Should NaN be treated as Num type?
- [ ] Maybe get rid of static char buffer for printing for send var functions?
- [ ] debugger commands accept any text with the command as a prefix

261
doc/notes/import syntax.md Normal file
View File

@ -0,0 +1,261 @@
So we need some syntax to distinguish between a relative import and a logical
import. I'm not sure which way to go, and I'd like some feedback (or possibly
other alternate ideas I haven't considered).
My two favorites are:
```
// Use
use "relative/path"
import "logical/path"
// Node-style
import "./relative/path"
import "logical/path"
```
If you folks are OK with "use", that's my preference. But otherwise, the Node
style will definitely work too. I'm open to other ideas as well, including a few
below, but I'd like to not bikeshed this forever.
## Background
There are four general approaches we can take:
### Use a modifier ("modifier")
Both kinds of imports start with `import`, but then we use a second keyword
afterwards to identify either a relative or logical import. We could use *two*
keywords -- one for each kind -- but that's unnecessarily verbose. Instead, we
use the presence or absence of the keyword to distinguish. In other words:
```
import foo "string"
import "string"
```
The specific questions we have to answer are:
1. Which kind of import gets the keyword? Ideally, the most common kind of
import would be the one that doesn't need an extra keyword.
2. What keyword? This is surprisingly hard. Probably some kind of preposition.
### Use different keywords ("keyword")
Instead of using `import` for both logical and relative imports, we could have
two keywords, one for each kind. The specific questions to answer then are:
1. Which kind of import gets `import`?
2. What's the other keyword?
### Use different syntax for the path ("syntax")
Instead of always using a string literal to identify what's being imported, we
could use a different kind of token or tokens for the different kinds of import.
For example, a string literal for one kind, and an identifier token for the
other:
import identifier
import "string literal"
The specific questions are:
1. Which kind of import uses a string literal?
2. What's the syntax for the other kind?
### Use a signifier in the import string itself to distinguish ("string")
An import is always `import` followed by a string literal. Then we use some
specific markers inside the string literal itself to distinguish the two kinds.
For example, Node says that an import string starting with "./" or "../" is
relative and other import strings are logical.
The specific question to answer is what kind of signifier we'd use. I think
Node's convention is the only real contender here, though.
One feature this style has that none of the others do is that it means the
language syntax itself has no notion of logical and relative imports. This
means there is no overhead or complexity for host applications where that
distinction isn't meaningful.
## Contenders
These are options I'm open to, in roughly descending order of preference:
### Node-style (string)
If the string starts with "./" or "../", it's relative.
```
import "./relative/path"
import "logical/path"
```
This is how Node works, so there's prior art. It keeps the language completely
simple. It does feel sort of arbitrary and magical to me, but it's the simplest,
most expedient solution.
### Use (keyword)
The `use` keyword is for relative imports, `import` is for logical.
```
use "relative/path"
import "logical/path"
```
The `use` keyword comes from Pascal, but that's not very widely known. I kind
of like this. It's short, and `use` feels "nearer" to me than "import" so it
has the right connotation. (You can't "use" something unless you have it near
to hand.)
It adds a little complexity to the language and VM. We have to support both
keywords and pass that "use versus import" bit through the name resolution
process. But that's pretty minor.
### Slashes (syntax)
If the path is a string literal, it's relative. Otherwise, it is a
slash-separated series of unquoted identifiers.
```
import "relative/path"
import logical/path
```
This means you can't (easily) use reserved words as names of logical imports.
This was my initial pitch. I still like how it looks, but I seem to be in the
minority.
### Relative (modifier)
The `relative` modifier is for relative imports.
```
import relative "relative/path"
import "logical/path"
```
It's explicit, which is good. It is unfortunately verbose. I think `relative`
is too useful of a word to make into a reserved word, which means it would have
to be a contextual keyword (i.e. treated like a reserved word after `import`
but behaving like a regular identifier elsewhere). I'm not generally a fan of
contextual keywords—they tend to make things like syntax highlighters more
difficult to create—so I try to avoid them.
## Rejected
I considered these ideas, but don't think they are good enough approaches for
various reasons:
### Package identifier (syntax)
If an unquoted identifier appears before the import string, then it's a logical
import within that package. Otherwise, it's relative.
```
import "relative/path"
import logical "path"
```
This was one of my initial ideas. It has the same problem as other unquoted
imports in that it makes it harder to have odd package names. It means the VM
has to understand this syntax and figure out how to display package names in
stack traces and stuff, so there is some extra complexity involved.
The form where you have both a package name and a relative path within that
package is pretty unusual and likely unintuitive to users.
### Dotted (syntax)
If the path is a string literal, it's relative. Otherwise, it is a
dot-separated series of unquoted identifiers.
```
import "relative/path"
import logical.path
```
Similar to slashes, but using dots. This helps make logical imports look more
visually distinct from relative ones. But it also makes them look more similar
to getter calls, which they aren't related to at all.
### Include (keyword)
The `include` keyword is for relative imports, `import` is for logical.
```
include "relative/path"
import "logical/path"
```
Ruby uses `include` for applying mixins. "Include" reads to me more like some
kind of transclusion thing, so it feels a little weird.
### Require (keyword)
The `require` keyword is for relative imports, `import` is for logical.
```
require "relative/path"
import "logical/path"
```
Node uses "require" and ES6 uses "import" so this is kind of confusing. Ruby
uses `require` and `require_relative`, so using `require` for a relative import
is kind of confusing. Lua also uses `require`, but for both relative and
logical. Overall, this feels murky and unhelpful to me.
### Angle-brackets (syntax)
As in C/C++, an import string can be in angle brackets or quotes. Angle brackets
are for logical imports, quotes for relative.
```
import "relative/path"
import <logical/path>
```
Hard pass. It requires context-sensitive tokenization (!) in C and we definitely
don't want to go there.
### URI scheme (string)
An import string starting with "package:" and maybe "wren:" is treated as
logical, like they are URIs with an explicit scheme. Others are relative.
```
import "relative/path"
import "package:logical/path"
import "wren:random"
```
This is (roughly) how Dart works. I'm not a fan. I think it's too verbose for
logical imports.
### Package (modifier)
A `package` modifier indicates a logical import. Others are relative.
```
import "relative/path"
import package "logical/path"
```
Pretty long, and I'm not too crazy about baking "package" into the language and
VM.
### From (modifier)
A `from` modifier indicates, uh, one kind of import.
```
import "some/path"
import from "other/path"
```
It looks nice, but it's totally unclear to me whether logical imports should
get `from` or relative ones. Also kind of confusing in that Python and ES6 use
`from` in their notation for importing explicit variables from a module (where
Wren uses `for`).

144
doc/notes/re-entrancy.md Normal file
View File

@ -0,0 +1,144 @@
## wrenInterpret()
You can already call out to a foreign method or constructor from within an
execution that was started using `wrenInterpret()`, so I think that's fine.
`wrenInterpret()` doesn't use the API stack at all.
## wrenCall()
Normally, when using `wrenCall()` to start executing some code, the API slots
are at the very bottom of the fiber's stack and the fiber has no other
callframes until execution begins.
When a foreign method or constructor is called, there *are* callframes on the
fiber's stack. There must be, because that's where the arguments to the foreign
method are.
So, if you `wrenCall()`, which eventually calls a foreign method, the same fiber
will be used for the API twice. This is currently broken. The reason it's broken
is that `callForeign()` and `createForeign()` store the old apiStack pointer
(the one used for the initial `wrenCall()`) in a local variable and then restore
it when the foreign call completes. If a GC or stack grow occurs in the middle
of that, we end up restoring a bad pointer.
But I don't think we need to preserve apiStack for the `wrenCall()` anyway. As
soon as the user calls `wrenCall()` and it starts running, we no longer need to
track the number of slots allocated for the API. All that matters is that the
one return value is available at the end.
I think this means it *should* be fairly easy to support:
wrenCall() -> wren code -> foreign method
## Foreign calls
The interesting one is whether you can call `wrenInterpret()` or `wrenCall()`
from within a foreign method. If we're going to allow re-entrancy at all, it
would be nice to completely support it. I do think there are practical uses
for this.
Calling `wrenInterpret()` should already work, though I don't think it's tested.
Calling `wrenCall()` is probably broken. It will try to re-use the slots that
are already set up for the foreign call and then who knows what happens if you
start to execute.
I think a key part of the problem is that we implicitly create or reuse the API
stack as soon as you start messing with slots. So if there already happens to
be an API stack -- because you're in the middle of a foreign method -- it will
incorrectly reuse it when you start preparing for the `wrenCall()`.
An obvious fix is to add a new function like `wrenPrepareCall()` that explicitly
creates a new API stack -- really a new fiber -- for you to use. We still have
to figure out how to keep track of the current API stack and fiber for the
foreign call so that we can return to it.
**TODO: more thinking here...**
If I can figure this out, it means we can do:
foreign method -> C code -> wrenCall()
## Nested foreign calls
If we compose the above it leads to the question of whether you can have
multiple nested foreign calls in-progress at the same time. Can you have a C
stack like:
wrenCall()
runInterpreter()
foreignCall()
wrenCall()
runInterpreter()
foreignCall()
...
This does *not* mean there is a single Wren stack that contains multiple
foreign calls. Since each `wrenCall()` begins a new fiber, any given Wren stack
can only ever have a single foreign API call at the top of the stack. I think
that's a good invariant.
I believe we should support the above. This means that the core
`runInterpreter()` C function is itself re-entrant. So far, I've always assumed
it would not be, so it probably breaks some assumptions. I'll have to think
through. The main thing that could be problematic is the local variables inside
`runInterpreter()`, but I believe `STORE_FRAME()` and `LOAD_FRAME()` take care
of those. We just need to make sure they get called before any re-entrancy can
happen. That probably means calling them before we invoke a foreign method.
I'll have to write some tests and see what blows up for this.
## Calling re-entrant fibers
Where it gets really confusing is how re-entrant calls interact with fibers.
For example, say you:
wrenCall() -> creates Fiber #1
runInterpreter() -> runs Fiber #1
some Wren code stores current fiber in a variable
foreignCall()
wrenCall() -> creates Fiber #2
runInterpreter() -> runs Fiber #2
some Wren code calls or transfers to Fiber #1
What happens in this scenario? We definitely want to prevent it. We already
detect and prevent the case where you call a fiber that's already called in the
current *Wren* stack, so we should be able to do something in the above case
too.
Now that I think about it, you can probably already get yourself in a weird
state if you grab the root fiber and call it. Yeah, I justed tested. This:
var root = Fiber.current
Fiber.new {
root.call()
System.print(1)
}.call()
System.print(2)
Segfaults the VM. :( It actually dies when the called child fiber *returns*. The
root call successfully continues executing the root fiber (which is super
weird). Then that completes and control returns to the spawned fiber. Then
*that* completes and tries to return control to the root fiber, but the root is
already done, and it blows up. So the above prints "2" then "1" then dies.
(If either of the `call()` calls are change to `transfer()`, the script runs
without any problems because then it never tries to unwind back through the
root fiber which already completed.)
To fix this, when `runInterpreter()` begins executing a root fiber (either from
`wrenCall()` or `wrenInterpret()`), we need to mark it in some way so that it
can't be called or transferred to.
## Suspending during re-entrancy
Maybe the weird conceptual case is when you suspend a fiber while there are
multiple re-entrant calls to `runInterpreter()` on the C stack. Ideall, they
would all magically return, but that's obviously not feasible.
I guess what will/should happen is that just the innermost one suspends. It's
up to the host to handle that fact. I need to think about this more, add some
tests, and work through it.
I think we'll probably want to add another WrenInterpretResult case for
suspension so that the host can tell that's what happened.

View File

@ -0,0 +1,478 @@
# Smarter Imports
**Note: This is now mostly implemented, though the implementation differs
somewhat from this original proposal.**
Here's a proposal for improving how imported modules are identified and found
to hopefully help us start growing an ecosystem of reusable Wren code. Please
do [let me know][list] what you think!
[list]: https://groups.google.com/forum/#!forum/wren-lang
## Motivation
As [others][210] [have][325] [noted][346], the way imports work in Wren,
particularly how the CLI resolves them, makes it much too hard to reuse code.
This proposal aims to improve that. It doesn't intend to fix *everything* about
imports and the module system, but should leave the door open for later
improvements.
[210]: https://github.com/wren-lang/wren/issues/210
[325]: https://github.com/wren-lang/wren/issues/325
[346]: https://github.com/wren-lang/wren/issues/346
### Relative imports
Today, it's hard to reuse your own code unless you literally dump everything in
a single directory. Say you have:
```text
script_a.wren
useful_stuff/
script_b.wren
thing_1.wren
thing_2.wren
```
`script_a.wren` and `script_b.wren` are both scripts you can run directly from
the CLI. They would both like to use `thing_1.wren`, which in turn imports
`thing_2.wren`. What does `thing_1.wren` look like? If you do:
```scala
// thing_1.wren
import "thing_2"
```
Then it works fine if you run `script_b.wren` from the `useful_stuff/`
directory. But if you try to run `script_a.wren` from the top level directory,
then it looks for `thing_2.wren` *there* and fails to find it. If you change the
import to:
```scala
// thing_1.wren
import "useful_stuff/thing_2"
```
Then `script_a.wren` works, but now `script_b.wren` is broken. The problem is
that all imports are treated as relative to the directory containing the
*initial script* you run. That means you can't reuse modules from scripts that
live in different directories.
In this example, if feels like imports should be treated as relative to the
file that contains the import statement. Often you want to specify, "Here is
*where* this other module is, relative to where *I* am."
### Logical imports
If we make imports relative, is that enough? Should *all* imports be relative? I
don't think so. First of all, some modules are not even on the file system.
There is no relative path that will take you to "random" — it's built into the
VM itself. Likewise, "io" is baked into the CLI.
Today, when you write:
```scala
import "io"
```
You aren't saying *where* that module should be found, you're saying *what*
module you want. Assuming we get a package manager at some point, these kinds of
"logical" imports will be common. So I want these too.
If you look at other langauges' package managers, you'll find many times a
single package offers a number of separate libraries you can use. So I also
want to support logical imports that contain a path too — the import would say
both *what* package to look in and *where* in that package to look.
### Only logical imports?
Given some kind of package-y import syntax, could we get rid of relative imports
and use those for everything? You'd treat your own program like it was itself
some kind of package and anything you wanted to import in it you'd import
relative to your app's root directory.
The problem is that the "root directory" for your program's "package" isn't
well-defined. We could say it's always the same directory as the script you're
running, but that's probably too limiting. You may want to run scripts that live
in subdirectories.
We could walk up the parent directories looking for some kind of "manifest" file
that declares "the root of the package is here", but that seems like a lot of
hassle if you just want to create a couple of text files and start getting some
code running. So, for your own programs, I think it's nice to still support
"pure" relative imports.
### Ambiguity?
OK, so we want both relative imports and logical imports. Can we use the same
syntax for both? We could allow, say:
```scala
import "a/b"
```
And the semantics would be:
1. Look for a module "a/b.wren" relative to the file containing the import. If
found, use it.
2. Otherwise, look inside some "package" directory for a package named "a" and
a module named "b.wren" inside it. If found use that.
3. Otherwise, look for a built in module named "a".
This is pretty much how things work now, but I don't think it's a good idea.
Relative imports will tend to be short — often single words like "utils".
Assuming we get a healthy package ecosystem at some point, the chances of one of
those colliding with a logical import name are high.
Also, when reading code, I think it's important to be able to easily tell "this
import is from my own program" without having to know the names of all of the
files and directories in the program.
## Proposal
OK, so here's my goals:
1. A way to import a module relative to the one containing the import.
2. A way to import a module from some named logical package, possibly at a
specific path within that package.
3. Distinct syntaxes for each of these.
I tried a few different ideas, and my favorite is:
### Relative imports
Relative imports use the existing syntax:
```scala
// Relative path.
import "ast/expr"
```
This looks for the file `ast/expr.wren` relative to the directory containing the
module that has this import statement in it.
You can also walk out of directories if you need to import a module in a parent
folder:
```scala
import "../../other/stuff"
```
### Logical imports
If you want to import a module from some named logical entity, you use an
*unquoted* identifier:
```scala
import random
```
Being unquoted means the names must be valid Wren identifiers and can't be
reserved words. I think that's OK. It would confuse the hell out of people if
you had a library named "if". I think the above *looks* nice, and the fact that
it's not quoted sends a signal (to me at least) that the name is a "what" more
than a "where".
If you want to import a specific module within a logical entity, you can have a
series of slash-separate identifiers after the name:
```scala
import wrenalyzer/ast/expr
```
This imports module "ast/expr" from "wrenalyzer".
## Implementation
That's the proposed syntax and basic semantics. The way we actually implement it
is tricky because Wren is both a standalone interpreter you can run on the
command line and an embedded scripting language. We have to figure out what goes
into the VM and what lives in the CLI, and the interface between the two.
### VM
As usual, I want to keep the VM minimal and free of policy. We do need to add
support for the new unquoted syntax. The more significant change is to the API
the VM uses to talk to the host app when a module is imported. The VM doesn't
know how to actually load modules. When it executes an import statement, it
calls:
```c
char* loadModuleFn(WrenVM* vm, const char* name);
```
The VM tells the host app the import string and the host app returns the code.
In order to distinguish relative imports (quoted) from an identical unquoted
name and path, we need to pass in an extra to bit to tell the host whether there
were quotes or not.
The more challenging change (and the reason I didn't support them when I first
added imports to Wren) is relative imports. There are two tricky parts:
First, the host app doesn't have enough context to resolve a relative import.
Right now, the VM only passes in the import string. It doesn't tell which module
*contains* that import string, so the host has no way of knowing what that
import should be relative *to*.
That's easy to fix. We have the VM pass in the name of the module that contains
the import.
The harder problem is **canonicalization**. When you import the same module
twice, the VM ensures it is only executed once and both places use the same
module data. This is important to ensure you don't get confusing things like
duplicate static state or other weird side effects.
To do that, the VM needs to be able to tell when two imports refer to the "same"
module. Right now, it uses the import string itself. If two imports use the same
string, they are the same module.
With relative imports, that is no longer valid. Consider:
```text
script_a.wren
useful_stuff/
thing_1.wren
thing_2.wren
```
Now imagine those files contain:
```scala
// script_a.wren
import "useful_stuff/thing_1"
import "useful_stuff/thing_2"
// useful_stuff/thing_1.wren
import "thing_2"
// useful_stuff/thing_2.wren
// Stuff...
```
Both `script_a.wren` and `thing_1` import `thing_2`, but the import *strings*
are different. The VM needs to be able to figure out that those two imports
refer to the same module. I don't want path manipulation logic in the VM, so it
will delegate to the host app for that as well.
Given the import string and the name of the module containing it, the host app
produces a "fully-qualified" or "canonical" name for the imported module. It is
*that* resulting string that the VM uses to tell if two imports resolve to the
same module. (It's also the string it uses in things like stack traces.)
This means importing becomes a three stage process:
1. First the VM asks the host to resolve an import. It gives it the (previously
resolved) name of the module containing the import, the imports string, and
whether or not it was quoted. The host app returns a canonical string for
that import.
2. The VM checks to see if a module with that canonical name has already been
imported. If so, it reuses that and its done.
3. Otherwise, it circles back and asks the host for the source of the module
with that given canonical name. It compiles and executes that and goes from
there.
So we add a new callback to the embedding API. Something like:
```c
char* resolveModuleFn(WrenVM* vm,
// Canonical name of the module containing the import.
const char* importer,
// The import string.
const char* path,
// Whether the path name was quoted.
bool isQuoted);
```
The VM invokes this for step one above. The other two steps are the existing
loading logic but now using the canonicalized string.
### CLI
All of the policy lives over in the CLI (or in your app if you are embedding the
VM). You are free to use whatever canonicalization policy makes sense for you.
For the CLI, and for the policy described up in motivation, it's something like
this:
* Imports are slash-separated paths. Resolving a relative path is normal path
joining relative to the directory containing the import. So if you're
importing "a/b" from "c/d" (which is a file named "d.wren" in a directory
"c"), then the canonical name is "c/a/b" and the file is "c/a/b.wren".
".." and "." are allowed and are normalized. So these imports all resolve
to the same module:
```scala
import "a/b/c"
import "a/./b/./c"
import "a/d/../b/c"
```
* If an import is quoted, the path is considered relative to the importing
module's path, and is in the same package as the importing module.
So, if the current file is "a/b/c.wren" in package "foo" then these are
equivalent:
```scala
import "d/e"
import foo/a/b/d/e
```
* If an import is unquoted, the first identifier is the logical "package"
containing the module, and the remaining components are the path within that
package. The canonicalized string is the logical name, a colon, then the
resolved full path to the import (without the ".wren" file extension).
So if you import:
```scala
import wrenalyzer/ast/expr
```
The canonical name is "wrenalyzer:ast/expr".
* If an import is a single unquoted name, the CLI implicitly uses the name as
the module to look for within that package. These are equivalent:
```scala
import foo
import foo/foo
```
We could use some default name like "module" instead of the package name,
similar to Python, but I think this is actually a little more usable in
practice. If you're hacking on a bunch of packages at the same time, it's
annoying if every tab in your text editor just says "module.wren".
* The canonicalized string for the main script or a module imported using a
relative path from the main script is just the normalized file path,
probably relative to the working directory.
* Since colon is used to separate the name from path, path components with
colons are not allowed.
### Finding logical imports
The last remaining piece is how the CLI physically locates logical imports. If
you write:
```scala
import foo
```
Where does it look for "foo"? Of course, if "foo" is built into the VM like
"random", then that's easy. Likewise, if it's built into the CLI like "io",
that's easy too.
Otherwise, it will try to find it on the file system. We don't have a package
manager yet, so we need some kind of simple policy so you can "hand-author" the
layout a package manager would produce. Borrowing from Node, the basic idea is
pretty simple.
To find a logical import, the CLI starts in the directory that contains the main
script (not the directory containing the module doing the import), and looks for
a directory named "wren_modules". If not found there, it starts walking up
parent directories until it finds one. If it does, it looks for the logical
import inside there. So, if you import "foo", it will try to find
"wren_modules/foo/foo.wren".
Once it finds a "wren_modules" directory, it uses that one directory for all
logical imports. You can't scatter stuff across multiple "wren_modules" folders
at different levels of the hierarchy. If it can't find a "wren_modules"
directory, or it can't find the requested module inside the directory, the
import fails.
This means that to reuse someone else's Wren "package" (or your own for that
matter), you can just stick a "wren_modules" directory next to the main script
for your app or in some parent directory. Inside that "wren_modules" directory,
copy in the package you want to reuse. If that package in turn uses other
packages, copy those into the *same* "wren_modules" directory. In other words,
the transitive dependencies get flattened. This is important to handle shared
dependencies between packages without duplication.
You only need to worry about all of this if you actually have logical imports.
If you just have a couple of files that import each other, you can use straight
relative imports and everything just works.
## Migration
OK, that's the plan. How do we get there? I've start hacking on the
implementation a little and, so far, it seems straightforward. Honestly, it will
probably take less time than I spent writing this up.
The tricky part is that this is a breaking change. All of your existing quoted
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 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
imports, you'll probably need to tweak them.
If you are hosting Wren in your own app, the imports are fine since your app
has control over how they resolve. But you will have to fix your app a little
since the import embedding API is going to change to deal with canonicalization.
I think I can make it so that if you don't provide a canonicalization callback,
then the original import string is treated as the canonical string and you
fall back to the current behavior.
## Alternatives
Having both quoted and unquoted import strings is a little funny, but it's the
best I could come up with. For what it's worth, I [borrowed it from
Racket][racket].
[racket]: https://docs.racket-lang.org/guide/module-basics.html
I considered a couple of other ideas which are potentially on the table if
most of you don't dig the main proposal:
### Node-style
In Node, [all imports are quoted][node]. To distinguish between relative and
logical imports, relative imports always start with "./". In Wren, it would be:
[node]: https://nodejs.org/api/modules.html
```scala
import "./something/relative"
import "logical/thing"
```
This is simpler than the main proposal since there are no syntax changes and we
don't need to push the "was quoted?" bit through the embedding API. But I find
the "./" pretty unintuitive especially if you're not steeped in the UNIX
tradition. Even if you are, it's weird that you *need* to use "./" when it means
nothing to the filesystem.
### Unquoted identifiers
The other idea I had was to allow both an unquoted identifier and a quoted
path, like:
```scala
import wrenalyzer "ast/expr"
```
The unquoted name is the logical part — the package name. The quoted part is
the path within that logical package. If you omit the unquoted name, it's a
straight relative import. If you have a name but no path, it's desugars to use
the name as the path.
This is a little more complex because we have to pass around the name and path
separately between the VM and the host app during canonicalization. If we want
the canonicalized form to keep those separate as well, then the way we keep
track of previously-loaded modules needs to get more complex too. Likewise the
way we show stack traces, etc.
The main proposal gloms everything into a single string using ":" to separate
the logical name part from the path. That's a little arbitrary, but it keeps
the VM a good bit simpler and means the idea of there being a "package name" is
pure host app policy.

View File

@ -0,0 +1,69 @@
^title Hello Wren
4 Feb 2019
---
Welcome to the new Wren development blog!
Around November 2018 on the Wren mailing list, munificent announced that a new maintainer is taking over the development and maintainence of the Wren language. [The original post is here, with all the details.](https://groups.google.com/forum/#!topic/wren-lang/cMUwij-NIn0)
In short, [I'm (ruby0x1)](https://github.com/ruby0x1) taking over from [Bob (munificent)](https://github.com/munificent) as maintainer, but Bob is sticking around as a contributor!
### The Wren blog
One of the first things I felt Wren needed going forward is a consistent and centralized place to talk about the language. The existing design choices and goals, and especially the future and evolution of Wren are something a lot of people want to read about, in detail. Now we have a place to do exactly that!
The blog will be keeping in the spirit of Wren by remaining simple. Posts are just regular markdown files in the repository alongside the rest of the site, and are considered part of the documentation.
Since Wren as a project aims to help others learn, having the in depth thought processes, development details and technical intricacy be documented in the same place, over a timeline, is valuable.
### What's next for Wren?
First and foremost, I wanted to state explicitly that **Wren is going to be changing** but it is **not going to become something else**.
Wren attracted me as a language because of what it _is_, not because it isn't {_other language_}. If I wanted to use {_other language_} I would have, but I chose Wren because I wanted what it was.
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 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.
There are immediate term goals, though.
### First steps
I think it's important to reset the baseline before we shake things up too much. Think of it as a ramp up to gain momentum, rather than running into a china store with arms flailing.
- We're gonna clear out a bit of backlog, tidying up issues and PRs
- Tidy up the website a bit, visually and structurally
- Make sure all documentation is up to date with the current development
- Tag 0.2.0 with a list of relevant changes since 0.1.0
Once we tag 0.2.0, we'll be in a good place to move forward. And, everything up until now will have a well defined checkpoint preserved, if people want to refer to it.
### Steps so far
Since the announcement and transition, I've been making my way through all the mailing list posts, issues and PRs in the backlog and reading all the way back to the early days.
I've also been talking to community members one on one and getting personal experiences and thoughts on Wren. Forming a full picture will help us since we'll have an overview of what's most relevant (and what isn't) as time has passed, and gives us actionable things to do for the next milestone. I think it's an important step.
We've also been investigating some of the PRs with the community to get those sorted out, since they're in the way.
Lastly, I've already done a bit of clean up on the website and documentation theme, added a new logo, and of course added the blog.
### Thanks!
Lastly, I wanted to say thanks to munificent, the community and all the contributors that have made Wren possible to this point. It's a wonderful thing and I look forward to seeing where we take it, together.
I hope you'll join us on the journey!
### More
- [The Wren Blog RSS](http://wren.io/blog/rss.xml)
- Join the [discord community](https://discord.gg/Kx6PxSX)
- Visit the [wren-lang organization](https://github.com/wren-lang) on GitHub.
- Follow [@munificentbob](https://twitter.com/munificentbob) or [@ruby0x1](https://twitter.com/ruby0x1) on twitter

View File

@ -0,0 +1,91 @@
^title 0.2.0 and beyond
30 Sep 2019
---
### 0.2.0 is here
It's time to tag a release!
Let's check our goals from [the last blog post](0-hello-wren.html):
- <s>We're gonna clear out a bit of backlog, tidying up issues and PRs</s>
- <s>Tidy up the website a bit, visually and structurally</s>
- <s>Make sure all documentation is up to date with the current development</s>
- <s>Tag 0.2.0 with a list of relevant changes since 0.1.0</s>
So far so good.
### Backlog
Clearing out the issues on a repo after some time has passed is always a bit tricky.
Many issues are outdated (or unrelated), and some need a proper response. Some are related to future ideals, things that will take a while to get to. Some are related to the Wren CLI. It can be difficult to reason about the state of the tasks when they're all over the place, so we've been consolidating.
The good news is the issue list has been drastically reduced, 70+ issues being closed (or resolved). Around 21 of those are marked for future consideration, and 23 moved to the Wren CLI repository. More consolidation will still continue.
**The goal is that the active issues are as relevant as possible in the immediate term.**
A tricky but important aspect to mention here is the perception of closing an issue...
A closed issue doesn't necessarily mean anything final, it's just a categorization tool!
The other categorization tool which operates _within_ open/closed categories, is the _label_. Labels allow us to distinguish clearly the different types of issues, which makes open/closed less binary and more nuanced and rich. We rely on both to make sense of the list.
For example, discussions of future tasks, ideas or goals [are tagged `future`](https://github.com/wren-lang/wren/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3Afuture+). Now we can refer to them later, and re-open them when they become active and relevant again.
**We're in this together.**
Please don't be discouraged if an issue is closed! Discussion is absolutely encouraged and ideas, proposals and input is very necessary. Feel free to keep a discussion going, even if the issue it's attached to has been marked as closed.
### 0.2.0
We've been hammering away on Wren since 0.1.0 for quite a while. The [change list on GitHub](https://github.com/wren-lang/wren/compare/0.1.0...5338275dcdd97fd8d9fc614f420a645500836a59) is too long to display!
Most importantly, before we start iterating on the language further, I wanted to make sure we had a checkpoint to look back to. That's largely what 0.2.0 is about.
There's quite a lot of good changes, with **290 commits from 41 contributors!**
Thanks to everyone getting involved, every little bit has helped Wren, no matter how small the contribution.
### 0.3.0
With 0.2.0 wrapped up, our next release won't be as far away this time.
**The primary goal for 0.3.0 is separating the VM from the CLI.**
This includes updated documentation, splitting the source repos, migrating all the tests, issues and more.
All the code and documentation will still be easy to access in one place, but clarity around Wren as a project will improve a lot.
The migration has already started, you can [find the wren-cli repository here](https://github.com/wren-lang/wren-cli).
I'm working on some of the refactoring on the [wren-cli-refactor branch.](https://github.com/wren-lang/wren/tree/wren-cli-refactor)
With that, we'll also have a cleaner build process for the CLI.
On some platforms (Windows especially), there have been several pain points, these will be addressed.
There's also gonna be an additional build target, namely emscripten, so we can easily run Wren examples on the Wren website and documentation.
And finally, we'll have some proper prebuilt releases with 0.3.0.
I know many people have just wanted to grab an executable and give the language a go, but that hasn't been an option.
We'll fix that with 0.3.0.
The 0.3.0 goals in simple form:
- VM / CLI split
- Build consistency/reliablity
- Web build for embedding in docs
- Prebuilt releases
### Beyond
I don't have any concrete plans for 0.4.0 right now, but once the dust settles from 0.3.0 we'll have a clearer view.
There's definitely things in the pipeline though, I've been playing with [adding compound assignments like `+=`](https://github.com/wren-lang/wren/pull/701).
More details about in development features and fixes can be found on the repo in the meantime.
Thanks for reading!
### More
- [The Wren Blog RSS](http://wren.io/blog/rss.xml)
- Join the [discord community](https://discord.gg/Kx6PxSX)
- Visit the [wren-lang organization](https://github.com/wren-lang) on GitHub to get involved.
- Follow the developers [@munificentbob](https://twitter.com/munificentbob) or [@ruby0x1](https://twitter.com/ruby0x1) on twitter

View File

@ -0,0 +1,135 @@
^title 0.3.0 released!
5 June 2020
---
In this post we'll cover 0.3.0 and the goals for 0.4.0 [#](#goals-for-0.4.0).
## About the 0.3.0 release
Let's revisit our goals from [the last blog post](1-0.2.0-and-beyond.html),
and mark what we managed to get done:
- <s>VM / CLI split</s> [#](#vm--cli-split)
- <s>Build consistency/reliablity</s> [#](#build-consistencyreliability)
- <s>Web build for embedding in docs</s> [#](#web-build-for-embedding-in-docs)
- <s>Prebuilt releases</s> [#](#prebuilt-releases)
## The details
### VM / CLI split
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 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.
The code structure is clearer, too, and all the tests and utils are now specific.
### Build consistency/reliability
Previously, builds on Windows could be a little fickle, and there was sometimes
issues with the dependencies on the CLI side.
To solve this, premake is now used to generate platform specific project files that
'just work', making it a one step process to build the VM or CLI. Both projects
now have a `projects/` folder which includes ready to go project files for primary platforms.
<small>The original `Makefile` and `util/wren.mk` no longer exist, so there might be some work needed
to reintegrate if you relied on those. You can find the updated makefile in `projects/make/`, or `projects/make.mac/`.</small>
The **amalgamated build** was fixed too, so that embedding in your own project is as simple as
including a single c file (and the `wren.h` header).
On the **CLI** side, the pre-build steps were removed and dependencies vendored in repo,
so that the project just builds with less potential points of error, especially across platforms.
And finally the **docs**! Previously [SASS](https://sass-lang.com/) was used, and code highlighting
was done at generation time using pygments, a python code highlighter. Both of these dependencies
have been removed, code highlighting is now done on the client side instead (see another reason why below).
The benefit here that it is now _easy_ to edit the docs, just a simple python command, no setup!
### Web build for embedding in docs
The goal was two part here, one is to have a page to just try out Wren.
Type in some code, run it. That's the first big step and we've now got that on the docs page.
<h4><a href="../try" target="_blank" class="dark-link">Try Wren directly in your browser!</a></h4>
This should work on desktop or mobile, and will continue to be improved over time.
The second part of that goal is having the VM available to make examples on each page interactive.
This is implemented, _but not activated on any pages yet_.
In the near future inline doc examples will have a small button that you can
press to see the code result right there, live. Since there's a lot of examples,
and sometimes they're fragments of code that don't run in isolation,
it will take time to propagate it through the pages.
Mainly, I didn't want this to hold up 0.3.0, but expect to start seeing it soon.
### Prebuilt releases
In addition to the browser based build that removes a barrier to trying out Wren,
Wren CLI has prebuilt binaries for Mac, Windows and Linux now! This gives
an easy path to just tinkering with Wren before embedding it.
---
## Goals for 0.4.0
With 0.4.0 the goal is to address a couple of bigger todos, but also to push the language
itself, and the embedding experience forward.
You can see some of the [work in progress tasks](https://github.com/wren-lang/wren/pulls?q=is%3Apr+is%3Aopen+label%3A0.4.0) here,
but there's a few things I'd like to resolve in 0.4.0.
**Compound operators**
I've really missed having `+=` and friends,
so I've been working on a (broken, wip) [PR here](https://github.com/wren-lang/wren/pull/701).
I've since had a better idea to implement it and will hope to address that in 0.4.0.
**Chained methods ('fluent interfaces')**
Currently in Wren it's required that the period (`.`) be on the same line as the method.
<pre class="snippet">
example.
some().
functions().
here()
</pre>
This isn't as elegant as we'd want for this form of API,
so **in 0.4.0 the goal is** allowing a newline, as you'd expect:
<pre class="snippet">
example
.some()
.functions()
.here()
</pre>
This doesn't seem like a big deal but when your calls are wider,
longer and possibly accept block functions. It's hard to read,
and can be less fun to track down a missing `.` in a big chunk of code.
<pre class="snippet">
example.
some {|args, and, stuff|
...
}.
here()
</pre>
**C Side APIs**
Some APIs for dealing with `Map` have been proposed several times,
it's time to bring that into the API. There's some additions for `List` as well,
like a helper to set an element in a list.
**Other goals**
There's a few more things but I'm still exploring their viability.
Keep an eye on the [PRs/issues](https://github.com/wren-lang/wren) or the [0.4.0 label](https://github.com/wren-lang/wren/pulls?q=is%3Apr+is%3Aopen+label%3A0.4.0) to see when they're discussed.
## Till next time
---
- [The Wren Blog RSS](http://wren.io/blog/rss.xml)
- Join the [discord community](https://discord.gg/Kx6PxSX)
- Visit the [wren-lang organization](https://github.com/wren-lang) on GitHub to get involved.
- Follow the developers [@munificentbob](https://twitter.com/munificentbob) or [@ruby0x1](https://twitter.com/ruby0x1) on twitter

View File

@ -0,0 +1,183 @@
^title 0.4.0 released!
8 April 2021
---
This post is all about the 0.4.0 release since it's a big one!
<small>(A separate post for 0.5.0 goals would likely come later.)</small>
## 0.4.0 details
**0.4.0 contains 145 commits from 28 contributors.**
The [full release notes](https://github.com/wren-lang/wren/releases/tag/0.4.0)
link to each PR or commit, and contains a lot more details than this post.
**Goals**
As usual, let's revisit the goals from the [0.3.0 post](2-0.3.0-released.html#goals-for-0.4.0).
Most importantly - compound operators didn't land in 0.4.0 for various reasons.
Still working on it, it's just a fun and nuanced problem and I don't want to
keep 0.4.0 back cos of it.
With that out the way, let's see what 0.4.0 contains!
## 0.4.0 highlights
Below we'll highlight some key features, fixes and improvements from the release.
**A lot of work came from the community, much thanks to everyone contributing!**
You can find all the details and the contributions in the [release notes](https://github.com/wren-lang/wren/releases/tag/0.4.0).
**Take note!** There are two minor breaking changes in the API on the release notes.
---
### Bug fixes
Several important bugs have been fixed, sneaky stack corruptions and some user
experience fixes that clarify confusing states.
### Documentation
A lot of work has gone into documentation this release, revising, fixing, adding
and closing gaps that were left. For example, Wren supports multi-line strings
but this was never mentioned anywhere!
### New **continue** keyword
Loops can now use continue, which is a welcome addition.
### New **as** keyword
You can now use `import "..." for Name as OtherName` to avoid name conflicts,
or to use aliases/shorthand for imported variables.
### Raw strings
Wren now supports triple quotes for a string `"""`.
This type of string is only unique in how it's parsed, the content of the
string is ignored (no interpolation or escapes are processed), which allows
complex strings to be expressed without needing to escape things.
A common example is json or regex, where there's a lot of escaping that obscures
the string content and makes it hard to read and maintain.
If they span multiple lines, the string ignores the open and closing newlines
and whitespace and preserves anything in between.
<pre class="snippet">
var json = """
{
"hello": "wren",
"from" : "json"
}
"""
</pre>
### Attributes
Attributes are user-defined metadata associated with a class or method that
can be used at runtime, by external tools (and potentially by Wren itself).
<pre class="snippet">
#hidden = true
#doc = "A simple example class"
class Example {}
</pre>
They can be:
- a `#key` on it's own
- a `#key = value`
- a `#group(with, multiple = true, keys = "value")`
**Example**
Below you can one obvious use case, a wip version where attributes for docs were
parsed and sent over to [vscode](https://code.visualstudio.com/) to display.
<video preload="auto" controls="" loop="loop" style="max-width:100%; width:auto; margin:auto; display:block;">
<source src="https://i.imgur.com/W9DWysP.mp4" type="video/mp4">
</video>
**Runtime access**
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 itself. You can access them via
`YourClass.attributes`. If any attributes are made available, they'll be found here:
- `YourClass.attributes.self` for the class attributes
- `YourClass.attributes.methods` for the method attributes
All the details for [Attributes can be found here](https://wren.io/classes.html#attributes).
### Chained methods fixes ('fluent interfaces')
Mentioned in the last post, you can now use this pattern in code as intended,
the same-line requirement for the `.` has been removed.
<pre class="snippet">
example
.some()
.functions()
.here()
</pre>
### List additions
Lists are now sortable via `list.sort()` and `list.sort {|a, b| ... }`.
You can find an index of something via `list.indexOf(value)`, and remove a value
via `list.remove(value)`. There's also `list.swap(index0, index1)` for moving
items around within a list.
For the API, `wrenSetListElement` now exists, and both set and
`wrenGetListElement` now accept negative indices same as the language side.
### Num additions
A few new constants:
- `Num.tau`
- `Num.nan`
- `Num.infinity`
- `Num.minSafeInteger`/`Num.maxSafeInteger`
And some new methods on a number:
- `num.min(other)`
- `num.max(other)`
- `num.clamp(min, max)`
- `num.cbrt`
- `num.exp`
- `num.log2`
### Map access from the API
You can now create and access maps from the API:
- `wrenSetSlotNewMap`
- `wrenGetMapCount`
- `wrenGetMapContainsKey`
- `wrenGetMapValue`
- `wrenSetMapValue`
- `wrenRemoveMapValue`
## Till next time
---
- [The Wren Blog RSS](http://wren.io/blog/rss.xml)
- Join the [discord community](https://discord.gg/Kx6PxSX)
- Visit the [wren-lang organization](https://github.com/wren-lang) on GitHub to get involved.
- Follow the developers [@munificentbob](https://twitter.com/munificentbob) or [@ruby0x1](https://twitter.com/ruby0x1) on twitter

View File

@ -0,0 +1,15 @@
^title Development blogs
[<h3>0.4.0 released!</h3>](3-0.4.0-released.html)
> <date>8 April 2021</date> • 0.4.0 is a big release, here's all the info!
[<h3>0.3.0 released!</h3>](2-0.3.0-released.html)
> <date>5 June 2020</date> • 0.3.0 release info! Plus some notes and goals for the next release, 0.4.0.
[<h3>0.2.0 and beyond</h3>](1-0.2.0-and-beyond.html)
> <date>30 Sep 2019</date> • Checkpoints, and the plans for 0.3.0.
[<h3>System.print("hello wren")</h3>](0-hello-wren.html)
> <date>4 Feb 2019</date> • A short post introducing the blog, the new maintainer, and the immediate term plans for Wren.

35
doc/site/blog/rss.xml Normal file
View File

@ -0,0 +1,35 @@
<rss version="2.0">
<channel><title>Wren - development blog</title>
<link>https://wren.io/</link>
<description>The development blog of the Wren programming language.</description>
<language>en-us</language>
<item>
<title>0.4.0 released</title>
<link>https://wren.io/blog/3-0.4.0-released.html</link>
<description>0.4.0 is a big release, here's all the info!</description>
<guid>https://wren.io/blog/3-0.4.0-released.html</guid>
<pubDate>Thu, 08 Apr 2021 00:00:00 GMT</pubDate>
</item>
<item>
<title>0.3.0 released</title>
<link>https://wren.io/blog/2-0.3.0-released.html</link>
<description>0.3.0 release info! Plus some notes and goals for the next release, 0.4.0.</description>
<guid>https://wren.io/blog/2-0.3.0-released.html</guid>
<pubDate>Mon, 05 Jun 2020 00:00:00 GMT</pubDate>
</item>
<item>
<title>0.2.0 and beyond</title>
<link>https://wren.io/blog/1-0.2.0-and-beyond.html</link>
<description>Checkpoints, and the plans for 0.3.0.</description>
<guid>https://wren.io/blog/1-0.2.0-and-beyond.html</guid>
<pubDate>Mon, 30 Sep 2019 00:00:00 GMT</pubDate>
</item>
<item>
<title>System.print("hello wren")</title>
<link>https://wren.io/blog/0-hello-wren.html</link>
<description>A short post introducing the blog, the new maintainer, and the immediate term plans for Wren.</description>
<guid>https://wren.io/blog/0-hello-wren.html</guid>
<pubDate>Mon, 04 Feb 2019 00:00:00 GMT</pubDate>
</item>
</channel>
</rss>

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<script type="application/javascript" src="../prism.js" data-manual></script>
<script type="application/javascript" src="../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../prism.css" />
<link rel="stylesheet" type="text/css" href="../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
the viewport. -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
</head>
<body id="top">
<header>
<div class="page">
<div class="main-column">
<h1><a href="../">wren</a></h1>
<h2>a classy little scripting language</h2>
</div>
</div>
</header>
<div class="page">
<nav class="big">
<a href="../"><img src="../wren.svg" class="logo"></a>
<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>
</nav>
<nav class="small">
<table>
<tr>
<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>
<main>
<h2>{title}</h2>
{html}
</main>
</div>
<footer>
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>
</footer>
</body>
</html>

View File

@ -17,8 +17,9 @@ stored in each instance.
Classes are created using the `class` keyword, unsurprisingly:
:::wren
class Unicorn {}
<pre class="snippet">
class Unicorn {}
</pre>
This creates a class named `Unicorn` with no methods or fields.
@ -26,42 +27,45 @@ This creates a class named `Unicorn` with no methods or fields.
To let our unicorn do stuff, we need to give it methods.
:::wren
class Unicorn {
prance() {
System.print("The unicorn prances in a fancy manner!")
}
}
<pre class="snippet">
class Unicorn {
prance() {
System.print("The unicorn prances in a fancy manner!")
}
}
</pre>
This defines a `prance()` method that takes no arguments. To add parameters, put
their names inside the parentheses:
:::wren
class Unicorn {
prance(where, when) {
System.print("The unicorn prances in " + where + " at " + when)
}
}
<pre class="snippet">
class Unicorn {
prance(where, when) {
System.print("The unicorn prances in %(where) at %(when).")
}
}
</pre>
Since the number of parameters is part of a method's [signature][] a class can
define multiple methods with the same name:
[signature]: method-calls.html#signature
:::wren
class Unicorn {
prance() {
System.print("The unicorn prances in a fancy manner!")
}
<pre class="snippet">
class Unicorn {
prance() {
System.print("The unicorn prances in a fancy manner!")
}
prance(where) {
System.print("The unicorn prances in " + where)
}
prance(where) {
System.print("The unicorn prances in %(where).")
}
prance(where, when) {
System.print("The unicorn prances in " + where + " at " + when)
}
}
prance(where, when) {
System.print("The unicorn prances in %(where) at %(when).")
}
}
</pre>
It's often natural to have the same conceptual operation work with different
sets of arguments. In other languages, you'd define a single method for the
@ -75,21 +79,24 @@ different syntaxes for methods. Your classes can define all of them.
A getter leaves off the parameter list and the parentheses:
:::wren
class Unicorn {
isFancy { true } // Unicorns are always fancy.
}
<pre class="snippet">
class Unicorn {
// Unicorns are always fancy.
isFancy { true }
}
</pre>
### Setters
A setter has `=` after the name, followed by a single parenthesized parameter:
:::wren
class Unicorn {
rider=(value) {
System.print("I am being ridden by " + value)
}
}
<pre class="snippet">
class Unicorn {
rider=(value) {
System.print("I am being ridden by %(value).")
}
}
</pre>
By convention, the parameter is usually named `value` but you can call it
whatever makes your heart flutter.
@ -98,36 +105,39 @@ whatever makes your heart flutter.
Prefix operators, like getters, have no parameter list:
:::wren
class Unicorn {
- {
System.print("Negating a unicorn is weird")
}
}
<pre class="snippet">
class Unicorn {
- {
System.print("Negating a unicorn is weird.")
}
}
</pre>
Infix operators, like setters, have a single parenthesized parameter for the
right-hand operand:
:::wren
class Unicorn {
-(other) {
System.print("Subtracting " + other + " from a unicorn is weird")
}
}
<pre class="snippet">
class Unicorn {
-(other) {
System.print("Subtracting %(other) from a unicorn is weird.")
}
}
</pre>
A subscript operator puts the parameters inside square brackets and can have
more than one:
:::wren
class Unicorn {
[index] {
System.print("Unicorns are not lists!")
}
<pre class="snippet">
class Unicorn {
[index] {
System.print("Unicorns are not lists!")
}
[x, y] {
System.print("Unicorns are not matrices either!")
}
}
[x, y] {
System.print("Unicorns are not matrices either!")
}
}
</pre>
Unlike with named methods, you can't define a subscript operator with an empty
parameter list.
@ -135,12 +145,13 @@ parameter list.
As the name implies, a subscript setter looks like a combination of a subscript
operator and a setter:
:::wren
class Unicorn {
[index]=(value) {
System.print("You can't stuff " + value + " into me at " + index)
}
}
<pre class="snippet">
class Unicorn {
[index]=(value) {
System.print("You can't stuff %(value) into me at %(index)!")
}
}
</pre>
## Method Scope
@ -153,13 +164,15 @@ are available on an object. When you write:
[scope]: variables.html#scope
[variables]: variables.html
:::wren
unicorn.isFancy
<pre class="snippet">
unicorn.isFancy
</pre>
You're saying "look up the method `isFancy` in the scope of the object
`unicorn`". In this case, the fact that you want to look up a *method* `isFancy`
and not a *variable* `isFancy` is explicit. That's what `.` does and the
object to the left of the period is the object you want to look up the method on.
`unicorn`&rdquo;. In this case, the fact that you want to look up a *method*
`isFancy` and not a *variable* `isFancy` is explicit. That's what `.` does and
the object to the left of the period is the object you want to look up the
method on.
### `this`
@ -167,22 +180,36 @@ Things get more interesting when you're inside the body of a method. When the
method is called on some object and the body is being executed, you often need
to access that object itself. You can do that using `this`.
:::wren
class Unicorn {
name { "Francis" }
<pre class="snippet">
class Unicorn {
name { "Francis" }
printName() {
System.print(this.name) //> Francis
}
}
printName() {
System.print(this.name) //> Francis
}
}
</pre>
The `this` keyword works sort of like a variable, but has special behavior. It
always refers to the instance whose method is currently being executed. This
lets you invoke methods on "yourself".
It's an error to refer to `this` outside of a method. However, it's perfectly
fine to use it inside a [function][] contained in a method. When you do, `this`
still refers to the instance whose *method* is being called.
fine to use it inside a [function][] declared *inside* a method. When you do,
`this` still refers to the instance whose *method* is being called:
<pre class="snippet">
class Unicorn {
name { "Francis" }
printNameThrice() {
(1..3).each {
// Use "this" inside the function passed to each().
System.print(this.name) //> Francis
} //> Francis
} //> Francis
}
</pre>
[function]: functions.html
@ -200,28 +227,30 @@ tedious and verbose, which is why some languages don't require it. You can do a
"self send" by calling a method (or getter or setter) without any explicit
receiver:
:::wren
class Unicorn {
name { "Francis" }
<pre class="snippet">
class Unicorn {
name { "Francis" }
printName() {
System.print(name) //> Francis
}
}
printName() {
System.print(name) //> Francis
}
}
</pre>
Code like this gets tricky when there is also a variable outside of the class
with the same name. Consider:
:::wren
var name = "variable"
<pre class="snippet">
var name = "variable"
class Unicorn {
name { "Francis" }
class Unicorn {
name { "Francis" }
printName() {
System.print(name) // ???
}
}
printName() {
System.print(name) // ???
}
}
</pre>
Should `printName()` print "variable" or "Francis"? A method body has a foot in
each of two worlds. It is surrounded by the lexical scope where it's defined in
@ -238,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
@ -247,24 +276,25 @@ work.
Here's an example that shows all three cases:
:::wren
var shadowed = "surrounding"
var lowercase = "surrounding"
var Capitalized = "surrounding"
<pre class="snippet">
var shadowed = "surrounding"
var lowercase = "surrounding"
var Capitalized = "surrounding"
class Scope {
shadowed { "object" }
lowercase { "object" }
Capitalized { "object" }
class Scope {
shadowed { "object" }
lowercase { "object" }
Capitalized { "object" }
test() {
var shadowed = "local"
test() {
var shadowed = "local"
System.print(shadowed) //> local
System.print(lowercase) //> object
System.print(Capitalized) //> surrounding
}
}
System.print(shadowed) //> local
System.print(lowercase) //> object
System.print(Capitalized) //> surrounding
}
}
</pre>
It's a bit of a strange rule, but Ruby works more or less the same way.
@ -275,12 +305,13 @@ Our unicorns can prance around, but we don't actually *have* any unicorns to do
it. To create *instances* of a class, we need a *constructor*. You define one
like so:
:::wren
class Unicorn {
construct new(name, color) {
System.print("My name is " + name + " and I am " + color + ".")
}
}
<pre class="snippet">
class Unicorn {
construct new(name, color) {
System.print("My name is " + name + " and I am " + color + ".")
}
}
</pre>
The `construct` keyword says we're defining a constructor, and `new` is its
name. In Wren, all constructors have names. The word "new" isn't special to
@ -288,20 +319,22 @@ Wren, it's just a common constructor name.
To make a unicorn now, we call the constructor method on the class itself:
:::wren
var fred = Unicorn.new("Fred", "palomino")
<pre class="snippet">
var fred = Unicorn.new("Fred", "palomino")
</pre>
Giving constructors names is handy because it means you can have more than one,
and each can clarify how it creates the instance:
:::wren
class Unicorn {
construct brown(name) {
System.print("My name is " + name + " and I am brown.")
}
}
<pre class="snippet">
class Unicorn {
construct brown(name) {
System.print("My name is " + name + " and I am brown.")
}
}
var dave = Unicorn.brown("Dave")
var dave = Unicorn.brown("Dave")
</pre>
Note that we have to declare a constructor because, unlike some other
languages, Wren doesn't give you a default one. This is useful because some
@ -314,10 +347,25 @@ 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:
:::wren
Unicorn.brown("Dave")
<pre class="snippet">
Unicorn.brown("Dave")
</pre>
That creates the new instance, then it invokes the *initializer* on that
instance. This is where the constructor body you defined gets run.
@ -331,12 +379,13 @@ constructors, etc.
All state stored in instances is stored in *fields*. Each field has a name
that starts with an underscore.
:::wren
class Rectangle {
area { _width * _height }
<pre class="snippet">
class Rectangle {
area { _width * _height }
// Other stuff...
}
// Other stuff...
}
</pre>
Here, `_width` and `_height` in the `area` [getter](classes.html#methods) refer
to fields on the rectangle instance. You can think of them like `this.width`
@ -355,27 +404,63 @@ value is `null`.
### Encapsulation
All fields are *private* in Wren&mdash;an object's fields can only be directly
accessed from within methods defined on the object's class. You cannot even
access fields on another instance of your own class, unlike C++ and Java.
accessed from within methods defined on the object's class.
If you want to make a property of an object visible, you need to define a
getter to expose it:
In short, if you want to make a property of an object visible,
**you need to define a getter to expose it**:
:::wren
class Rectangle {
width { _width }
height { _height }
<pre class="snippet">
class Rectangle {
width { _width }
height { _height }
// ...
}
// ...
}
</pre>
To allow outside code to modify the field, you'll also need to provide setters:
To allow outside code to modify the field,
**you need to provide setters to provide access**:
:::wren
class Rectangle {
width=(value) { _width = value }
height=(value) { _height = value }
}
<pre class="snippet">
class Rectangle {
width=(value) { _width = value }
height=(value) { _height = value }
}
</pre>
This might be different from what you're used to, so here are two important facts:
- You can't access fields from a base class.
- You can't access fields on another instance of your own class.
Here is an example in code:
<pre class="snippet">
class Shape {
construct new() {
_shape = "none"
}
}
class Rectangle is Shape {
construct new() {
//This will print null!
//_shape from the parent class is private,
//we are reading `_shape` from `this`,
//which has not been set, so returns null.
System.print("I am a %(_shape)")
//a local variable, all variables are private
_width = 10
var other = Rectangle.new()
//other._width is not accessible from here,
//even though we are also a rectangle. The field
//is private, and other._width is invalid syntax!
}
}
...
</pre>
One thing we've learned in the past forty years of software engineering is that
encapsulating state tends to make code easier to maintain, so Wren defaults to
@ -392,42 +477,46 @@ A name that starts with *two* underscores is a *static* field. They work
similar to [fields](#fields) except the data is stored on the class itself, and
not the instance. They can be used in *both* instance and static methods.
:::wren
class Foo {
construct new() {}
<pre class="snippet">
class Foo {
construct new() {}
static setFromStatic(a) { __a = a }
setFromInstance(a) { __a = a }
static setFromStatic(a) { __a = a }
setFromInstance(a) { __a = a }
static printFromStatic() {
System.print(__a)
}
static printFromStatic() {
System.print(__a)
}
printFromInstance() {
System.print(__a)
}
}
printFromInstance() {
System.print(__a)
}
}
</pre>
Just like instance fields, static fields are initially `null`:
:::wren
Foo.printFromStatic() //> null
<pre class="snippet">
Foo.printFromStatic() //> null
</pre>
They can be used from static methods:
:::wren
Foo.setFromStatic("first")
Foo.bar.printFromStatic() //> first
<pre class="snippet">
Foo.setFromStatic("first")
Foo.printFromStatic() //> first
</pre>
And also instance methods. When you do so, there is still only one static field
shared among all instances of the class:
:::wren
var foo1 = Foo.new()
var foo2 = Foo.new()
<pre class="snippet">
var foo1 = Foo.new()
var foo2 = Foo.new()
foo1.setFromInstance("second")
foo2.printFromInstance() //> second
foo1.setFromInstance("second")
foo2.printFromInstance() //> second
</pre>
## Inheritance
@ -435,14 +524,15 @@ A class can inherit from a "parent" or *superclass*. When you invoke a method
on an object of some class, if it can't be found, it walks up the chain of
superclasses looking for it there.
By default, any new class inherits from `Object`, which is the superclass from
By default, any new class inherits from Object, which is the superclass from
which all other classes ultimately descend. You can specify a different parent
class using `is` when you declare the class:
:::wren
class Pegasus is Unicorn {}
<pre class="snippet">
class Pegasus is Unicorn {}
</pre>
This declares a new class `Pegasus` that inherits from `Unicorn`.
This declares a new class Pegasus that inherits from Unicorn.
Note that you should not create classes that inherit from the built-in types
(Bool, Num, String, Range, List). The built-in types expect their internal bit
@ -450,32 +540,34 @@ representation to be very specific and get horribly confused when you invoke one
of the inherited built-in methods on the derived type.
The metaclass hierarchy does *not* parallel the regular class hierarchy. So, if
`Pegasus` inherits from `Unicorn`, `Pegasus`'s metaclass will not inherit from
`Unicorn`'s metaclass. In more prosaic terms, this means that static methods
are not inherited.
Pegasus inherits from Unicorn, Pegasus's metaclass does not inherit from
Unicorn's metaclass. In more prosaic terms, this means that static methods are
not inherited.
:::wren
class Unicorn {
// Unicorns cannot fly. :(
static canFly { false }
}
<pre class="snippet">
class Unicorn {
// Unicorns cannot fly. :(
static canFly { false }
}
class Pegasus is Unicorn {}
class Pegasus is Unicorn {}
Pegasus.canFly //! Static methods are not inherited.
Pegasus.canFly //! Static methods are not inherited.
</pre>
This also means constructors are not inherited:
:::wren
class Unicorn {
construct new(name) {
System.print("My name is " + name + ".")
}
}
<pre class="snippet">
class Unicorn {
construct new(name) {
System.print("My name is " + name + ".")
}
}
class Pegasus is Unicorn {}
class Pegasus is Unicorn {}
Pegasus.new("Fred") //! Pegasus does not define new().
Pegasus.new("Fred") //! Pegasus does not define new().
</pre>
Each class gets to control how it may be constructed independently of its base
classes. However, constructor *initializers* are inherited since those are
@ -483,20 +575,21 @@ instance methods on the new object.
This means you can do `super` calls inside a constructor:
:::wren
class Unicorn {
construct new(name) {
System.print("My name is " + name + ".")
}
}
<pre class="snippet">
class Unicorn {
construct new(name) {
System.print("My name is " + name + ".")
}
}
class Pegasus is Unicorn {
construct new(name) {
super(name)
}
}
class Pegasus is Unicorn {
construct new(name) {
super(name)
}
}
Pegasus.new("Fred") //> My name is Fred
Pegasus.new("Fred") //> My name is Fred
</pre>
## Super
@ -511,34 +604,162 @@ overridden.
To do that, you can use the special `super` keyword as the receiver in a method
call:
:::wren
class Base {
method() {
System.print("base method")
}
}
<pre class="snippet">
class Base {
method() {
System.print("base method")
}
}
class Derived is Base {
method() {
super.method() //> base method
}
}
class Derived is Base {
method() {
super.method() //> base method
}
}
</pre>
You can also use `super` without a method name inside a constructor to invoke a
base class constructor:
:::wren
class Base {
construct new(arg) {
System.print("base got " + arg)
}
}
<pre class="snippet">
class Base {
construct new(arg) {
System.print("base got " + arg)
}
}
class Derived is Base {
construct new() {
super("value") //> base got value
}
}
class Derived is Base {
construct new() {
super("value") //> base got value
}
}
</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(_,_): { null: { regular:[null] } } }
#!regular
regular(arg0, arg1) {}
// { static other(): { null: { isStatic:[true] } } }
#!isStatic = true
static other() {}
// { foreign static example(): { null: { isForeignStatic:[32] } } }
#!isForeignStatic=32
foreign static example()
}
</pre>
<br><hr>
<a class="right" href="concurrency.html">Concurrency &rarr;</a>
<a href="functions.html">&larr; Functions</a>

View File

@ -0,0 +1,30 @@
^title Wren CLI
---
## What is it?
**The Wren Command-Line Interface** is a tool you can run which gives you a way to run Wren code, and
also includes modules for talking to the operating system&mdash;file IO,
networking, stuff like that. It depends on [libuv][] for that
functionality.
Wren as a language is intentionally designed to be minimal.
That includes the built in language features, the standard library and the VM itself.
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 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.
## Why does it exist?
- It's fun to make things.
- It's always a good idea to test the language you're making!
- Interest was expressed in a scriptable tool using the Wren language.
- It's helpful for others to learn from, since it is a real world usage example showing several concepts.
[libuv]: http://libuv.org/

View File

@ -0,0 +1,16 @@
^title CLI Modules
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
of the command-line app, so can't easily be separated out and pulled into host
applications that want to embed Wren. Scripts written for the CLI then,
are specific to the CLI unless another host implements the same API.
[libuv]: http://libuv.org
* [io](io)
* [os](os)
* [scheduler](scheduler)
* [timer](timer)

View File

@ -16,10 +16,11 @@ Opens the file at `path` for writing and passes it to `fn`. If there is already
a file at that path, it is truncated. After the function returns, the file is
automatically closed.
:::wren
File.create("numbers.txt") {|file|
file.writeBytes("one two three")
}
<pre class="snippet">
File.create("numbers.txt") {|file|
file.writeBytes("one two three")
}
</pre>
### File.**delete**(path)
@ -35,17 +36,19 @@ or other special file system entities.
Opens the file at `path` for reading and passes it to `fn`. After the function
returns, the file is automatically closed.
:::wren
File.open("words.txt") {|file|
file.readBytes(5)
}
<pre class="snippet">
File.open("words.txt") {|file|
file.readBytes(5)
}
</pre>
### File.**read**(path)
Reads the entire contents of the file at `path` and returns it as a string.
:::wren
File.read("words.txt")
<pre class="snippet">
File.read("words.txt")
</pre>
No encoding or decoding is done. If the file is UTF-8, then the resulting
string will be a UTF-8 string. Otherwise, it will be a string of bytes in
@ -56,9 +59,10 @@ whatever encoding the file uses.
Resolves `path`, traversing symlinks and removining any unneeded `./` and `../`
components. Returns the canonical absolute path to the file.
:::wren
var path = "/some/./symlink/a/../b/file.txt"
System.print(File.realPath(path)) //> /real/path/a/file.txt
<pre class="snippet">
var path = "/some/./symlink/a/../b/file.txt"
System.print(File.realPath(path)) //> /real/path/a/file.txt
</pre>
### File.**size**(path)
@ -71,10 +75,11 @@ Returns the size in bytes of the contents of the file at `path`.
Opens the file at `path` for writing. If there is already a file at that path,
it is truncated.
:::wren
var file = File.create("colors.txt")
file.writeBytes("chartreuse lime teal")
file.close()
<pre class="snippet">
var file = File.create("colors.txt")
file.writeBytes("chartreuse lime teal")
file.close()
</pre>
### File.**open**(path)
@ -97,28 +102,30 @@ The size of the contents of the file in bytes.
### **close**()
Closes the file. After calling this, you can read or write from it.
Closes the file. After calling this, you can't read or write from it.
### **readBytes**(count)
Reads up to `count` bytes starting from the beginning of the file.
:::wren
// Assume this file contains "I am a file!".
File.open("example.txt") {|file|
System.print(file.readBytes(6)) //> I am a
}
<pre class="snippet">
// Assume this file contains "I am a file!".
File.open("example.txt") {|file|
System.print(file.readBytes(6)) //> I am a
}
</pre>
### **readBytes**(count, offset)
Reads up to `count` bytes starting at `offset` bytes from the beginning of
the file.
:::wren
// Assume this file contains "I am a file!".
File.open("example.txt") {|file|
System.print(file.readBytes(6, 2)) //> am a f
}
<pre class="snippet">
// Assume this file contains "I am a file!".
File.open("example.txt") {|file|
System.print(file.readBytes(6, 2)) //> am a f
}
</pre>
### **writeBytes**(bytes)

View File

@ -6,3 +6,4 @@ Provides access to operating system streams and the file system.
* [File](file.html)
* [Stat](stat.html)
* [Stdin](stdin.html)
* [Stdout](stdout.html)

View File

@ -16,6 +16,12 @@ Defaults to `false`.
Sets raw mode on or off.
### **isTerminal**
Returns `true` if Stdin is connected to a "TTY". This is true when the user is
running Wren in an interactive terminal, and false if it its input is coming
from a pipe.
### **readByte**()
Reads one byte of input from stdin. Blocks the current fiber until a byte has
@ -23,9 +29,17 @@ been received.
Returns the byte value as a number or `null` if stdin is closed.
Note that output is not automatically flushed when calling this. If you want to
display a prompt before reading input, you'll want to call `Stdout.flush()`
after printing the prompt.
### **readLine**()
Reads one line of input from stdin. Blocks the current fiber until a full line
of input has been received.
Returns the string of input or `null` if stdin is closed.
Note that output is not automatically flushed when calling this. If you want to
display a prompt before reading input, you'll want to call `Stdout.flush()`
after printing the prompt.

View File

@ -0,0 +1,11 @@
^title Stdout Class
The standard output stream.
## Static Methods
### **flush()**
Flushes all buffered data to the stream. Ensures any data written to stdout
that is in the buffer gets written to the file or terminal that stdout is
connected to.

View File

@ -3,7 +3,10 @@
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<link rel="stylesheet" type="text/css" href="../../style.css" />
<script type="application/javascript" src="../../../prism.js" data-manual></script>
<script type="application/javascript" src="../../../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
<link rel="stylesheet" type="text/css" href="../../../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
the viewport. -->
@ -13,16 +16,18 @@
<header>
<div class="page">
<div class="main-column">
<h1><a href="../../">wren</a></h1>
<h1><a href="../../../">wren</a></h1>
<h2>a classy little scripting language</h2>
</div>
</div>
</header>
<div class="page">
<nav class="big">
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Modules</a></li>
<li><a href="./">io</a></li>
<li><a href="../../">Back to Wren CLI</a></li>
<li><a href="../">Back to CLI Modules</a></li>
<li><a href="./">io module</a></li>
</ul>
<section>
<h2>io classes</h2>
@ -32,14 +37,15 @@
<li><a href="file-flags.html">FileFlags</a></li>
<li><a href="stat.html">Stat</a></li>
<li><a href="stdin.html">Stdin</a></li>
<li><a href="stdout.html">Stdout</a></li>
</ul>
</section>
</nav>
<nav class="small">
<table>
<tr>
<td><a href="../">Modules</a></td>
<td><a href="./">io</a></td>
<td><a href="../">Back to CLI Modules</a></td>
<td><a href="./">io module</a></td>
</tr>
<tr>
<td colspan="2"><h2>io classes</h2></td>
@ -56,6 +62,7 @@
<ul>
<li><a href="stat.html">Stat</a></li>
<li><a href="stdin.html">Stdin</a></li>
<li><a href="stdout.html">Stdout</a></li>
</ul>
</td>
</tr>
@ -70,10 +77,10 @@
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/munificent/wren">on GitHub</a>
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>

View File

@ -0,0 +1,7 @@
^title Module "os"
The os module exposes classes for accessing capabilities provided by the
underlying operating system.
* [Platform](platform.html)
* [Process](process.html)

View File

@ -0,0 +1,30 @@
^title Platform Class
The Platform class exposes basic information about the operating system Wren is
running on top of.
## Static Methods
### **name**
The name of the platform. This roughly describes the operating system, and is
usually one of:
* "iOS"
* "Linux"
* "OS X"
* "POSIX"
* "Unix"
* "Windows"
If Wren was compiled for an unknown operating system, returns "Unknown".
### **isPosix**
Returns `true` if the host operating system is known to support the POSIX
standard. This is true for Linux and other Unices, as well as the various Apple
operating systems.
### **isWindows**
Returns `true` if the host operating system is some flavor of Windows.

View File

@ -1,6 +1,6 @@
^title Process Class
The Process class lets you work with operating processes, including the
The Process class lets you work with operating system processes, including the
currently running one.
## Static Methods
@ -13,13 +13,13 @@ run (if any), and any other options passed to Wren itself.
If you run:
:::bash
$ wren file.wren arg
This returns:
:::wren
System.print(Process.allArguments) //> ["wren", "file.wren", "arg"]
<pre class="snippet">
System.print(Process.allArguments) //> ["wren", "file.wren", "arg"]
</pre>
### **arguments**
@ -29,10 +29,10 @@ itself.
If you run:
:::bash
$ wren file.wren arg
This returns:
:::wren
System.print(Process.arguments) //> ["arg"]
<pre class="snippet">
System.print(Process.arguments) //> ["arg"]
</pre>

View File

@ -3,7 +3,10 @@
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<link rel="stylesheet" type="text/css" href="../../style.css" />
<script type="application/javascript" src="../../../prism.js" data-manual></script>
<script type="application/javascript" src="../../../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
<link rel="stylesheet" type="text/css" href="../../../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
the viewport. -->
@ -13,20 +16,23 @@
<header>
<div class="page">
<div class="main-column">
<h1><a href="../../">wren</a></h1>
<h1><a href="../../../">wren</a></h1>
<h2>a classy little scripting language</h2>
</div>
</div>
</header>
<div class="page">
<nav class="big">
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Modules</a></li>
<li><a href="./">process</a></li>
<li><a href="../../">Back to Wren CLI</a></li>
<li><a href="../">Back to CLI Modules</a></li>
<li><a href="./">os module</a></li>
</ul>
<section>
<h2>process classes</h2>
<h2>os classes</h2>
<ul>
<li><a href="platform.html">Platform</a></li>
<li><a href="process.html">Process</a></li>
</ul>
</section>
@ -34,15 +40,16 @@
<nav class="small">
<table>
<tr>
<td><a href="../">Modules</a></td>
<td><a href="./">process</a></td>
<td><a href="../">Back to CLI Modules</a></td>
<td><a href="./">os module</a></td>
</tr>
<tr>
<td colspan="2"><h2>process classes</h2></td>
<td colspan="2"><h2>os classes</h2></td>
</tr>
<tr>
<td>
<ul>
<li><a href="platform.html">Platform</a></li>
<li><a href="process.html">Process</a></li>
</ul>
</td>
@ -62,10 +69,10 @@
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/munificent/wren">on GitHub</a>
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>

View File

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

View File

@ -0,0 +1,27 @@
^title Scheduler Class
The Scheduler class maintains a list of fibers, to be started one after the other, when a signal to do so is received. The signal (a private method call) is typically transmitted by _long running_ methods in the File or Timer classes which suspend the current fiber so that Wren can carry out other tasks in the meantime.
## Static Method
### 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>

View File

@ -3,7 +3,10 @@
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<link rel="stylesheet" type="text/css" href="../../style.css" />
<script type="application/javascript" src="../../../prism.js" data-manual></script>
<script type="application/javascript" src="../../../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
<link rel="stylesheet" type="text/css" href="../../../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
the viewport. -->
@ -13,16 +16,18 @@
<header>
<div class="page">
<div class="main-column">
<h1><a href="../../">wren</a></h1>
<h1><a href="../../../">wren</a></h1>
<h2>a classy little scripting language</h2>
</div>
</div>
</header>
<div class="page">
<nav class="big">
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Modules</a></li>
<li><a href="./">scheduler</a></li>
<li><a href="../../">Back to Wren CLI</a></li>
<li><a href="../">Back to CLI Modules</a></li>
<li><a href="./">scheduler module</a></li>
</ul>
<section>
<h2>scheduler classes</h2>
@ -34,8 +39,8 @@
<nav class="small">
<table>
<tr>
<td><a href="../">Modules</a></td>
<td><a href="./">scheduler</a></td>
<td><a href="../">Back to CLI Modules</a></td>
<td><a href="./">scheduler module</a></td>
</tr>
<tr>
<td colspan="2"><h2>scheduler classes</h2></td>
@ -62,10 +67,10 @@
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/munificent/wren">on GitHub</a>
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>

View File

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<script type="application/javascript" src="../../prism.js" data-manual></script>
<script type="application/javascript" src="../../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../../prism.css" />
<link rel="stylesheet" type="text/css" href="../../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
the viewport. -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
</head>
<body id="top" class="module">
<header>
<div class="page">
<div class="main-column">
<h1><a href="../../">wren</a></h1>
<h2>a classy little scripting language</h2>
</div>
</div>
</header>
<div class="page">
<nav class="big">
<a href="../../"><img src="../../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Back to Wren CLI</a></li>
</ul>
<section>
<h2>Built In</h2>
<ul>
<li><a href="../../modules">Wren modules</a></li>
</ul>
</section>
<section>
<h2>CLI modules</h2>
<ul>
<li><a href="io">io</a></li>
<li><a href="os">os</a></li>
<li><a href="scheduler">scheduler</a></li>
<li><a href="timer">timer</a></li>
</ul>
</section>
</nav>
<nav class="small">
<table>
<tr>
<td><h2>core</h2></td>
<td><h2>optional</h2></td>
<td><h2>cli</h2></td>
</tr>
<tr>
<td>
<ul>
<li><a href="core">core</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="meta">meta</a></li>
<li><a href="random">random</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="io">io</a></li>
<li><a href="os">os</a></li>
<li><a href="scheduler">scheduler</a></li>
<li><a href="timer">timer</a></li>
</ul>
</td>
</tr>
</table>
</nav>
<main>
<h1>{title}</h1>
{html}
</main>
</div>
<footer>
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>
</footer>
</body>
</html>

View File

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

View File

@ -3,7 +3,10 @@
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<link rel="stylesheet" type="text/css" href="../../style.css" />
<script type="application/javascript" src="../../../prism.js" data-manual></script>
<script type="application/javascript" src="../../../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
<link rel="stylesheet" type="text/css" href="../../../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
the viewport. -->
@ -13,16 +16,18 @@
<header>
<div class="page">
<div class="main-column">
<h1><a href="../../">wren</a></h1>
<h1><a href="../../../">wren</a></h1>
<h2>a classy little scripting language</h2>
</div>
</div>
</header>
<div class="page">
<nav class="big">
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Modules</a></li>
<li><a href="./">timer</a></li>
<li><a href="../../">Back to Wren CLI</a></li>
<li><a href="../">Back to CLI Modules</a></li>
<li><a href="./">timer module</a></li>
</ul>
<section>
<h2>timer classes</h2>
@ -34,8 +39,8 @@
<nav class="small">
<table>
<tr>
<td><a href="../">Modules</a></td>
<td><a href="./">timer</a></td>
<td><a href="../">Back to CLI Modules</a></td>
<td><a href="./">timer module</a></td>
</tr>
<tr>
<td colspan="2"><h2>timer classes</h2></td>
@ -62,10 +67,10 @@
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/munificent/wren">on GitHub</a>
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>

View File

@ -0,0 +1,12 @@
^title Timer Class
## Static Method
### 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.

View File

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<script type="application/javascript" src="../prism.js" data-manual></script>
<script type="application/javascript" src="../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../prism.css" />
<link rel="stylesheet" type="text/css" href="../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
the viewport. -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
</head>
<body id="top">
<header>
<div class="page">
<div class="main-column">
<h1><a href="../">wren</a></h1>
<h2>a classy little scripting language</h2>
</div>
</div>
</header>
<div class="page">
<nav class="big">
<a href="../"><img src="../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Back to Wren</a></li>
</ul>
<section>
<h2>Wren CLI</h2>
<ul>
<li><a href="./">About</a></li>
<li><a target="_blank" href="https://github.com/wren-lang/wren-cli/releases">Downloads</a></li>
<li><a href="usage.html">Usage</a></li>
</ul>
</section>
<section>
<h2>API docs</h2>
<ul>
<li><a href="modules">CLI Modules</a></li>
</ul>
</section>
</nav>
<nav class="small">
<table>
<tr>
<div><a href="../">Back to Wren</a></div>
</tr>
<tr>
<td><h2>CLI</h2></td>
<td><h2>API</h2></td>
</tr>
<tr>
<td>
<ul>
<li><a href="./">About</a></li>
<li><a target="_blank" href="https://github.com/wren-lang/wren-cli/releases">Downloads</a></li>
<li><a href="usage.html">Usage</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="modules">CLI Modules</a></li>
</ul>
</td>
</tr>
</table>
</nav>
<main>
<h2>{title}</h2>
{html}
</main>
</div>
<footer>
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>
</footer>
</body>
</html>

View File

@ -0,0 +1,55 @@
^title Wren CLI Usage
---
You can [download a build for your OS from the releases page](https://github.com/wren-lang/wren-cli/releases).
### Interactive mode
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.
Here's something to try:
<pre class="snippet">
System.print("Hello, world!")
</pre>
Or a little more exciting:
<pre class="snippet">
for (i in 1..10) System.print("Counting up %(i)")
</pre>
### Running scripts
The standalone interpreter can also load scripts from files and run them. Just
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">
for (yPixel in 0...24) {
var y = yPixel / 12 - 1
for (xPixel in 0...80) {
var x = xPixel / 30 - 2
var x0 = x
var y0 = y
var iter = 0
while (iter < 11 && x0 * x0 + y0 * y0 <= 4) {
var x1 = (x0 * x0) - (y0 * y0) + x
var y1 = 2 * x0 * y0 + y
x0 = x1
y0 = y1
iter = iter + 1
}
System.write(" .-:;+=xX$& "[iter])
}
System.print("")
}
</pre>
Now run:
$ ./wren_cli my_script.wren

View File

@ -23,37 +23,46 @@ fiber for every line of code you type in.
All Wren code runs within the context of a fiber. When you first start a Wren
script, a main fiber is created for you automatically. You can spawn new fibers
using the `Fiber` class's constructor:
using the Fiber class's constructor:
:::wren
var fiber = Fiber.new {
System.print("This runs in a separate fiber.")
}
<pre class="snippet">
var fiber = Fiber.new {
System.print("This runs in a separate fiber.")
}
</pre>
Creating a fiber does not immediately run it. It's just a first class bundle of
code sitting there waiting to be activated, a bit like
a [function](functions.html).
It takes a [function][] containing the code the fiber should execute. The
function can take zero or one parameter, but no more than that. Creating the
fiber does not immediately run it. It just wraps the function and sits there,
waiting to be activated.
[function]: functions.html
## Invoking fibers
Once you've created a fiber, you can invoke it (which suspends the current
fiber) by calling its `call()` method:
Once you've created a fiber, you run it by calling its `call()` method:
:::wren
fiber.call()
<pre class="snippet">
fiber.call()
</pre>
The called fiber will execute its code until it reaches the end of its body or
until it passes control to another fiber. If it reaches the end of its body,
it's considered *done*:
This suspends the current fiber and executes the called one until it reaches the
end of its body or until it passes control to yet another fiber. If it reaches
the end of its body, it is considered *done*:
:::wren
var fiber = Fiber.new { System.print("Hi") }
System.print(fiber.isDone) //> false
fiber.call()
System.print(fiber.isDone) //> true
<pre class="snippet">
var fiber = Fiber.new {
System.print("It's alive!")
}
When it finishes, it automatically resumes the fiber that called it. It's a
runtime error to try to call a fiber that is already done.
System.print(fiber.isDone) //> false
fiber.call() //> It's alive!
System.print(fiber.isDone) //> true
</pre>
When a called fiber finishes, it automatically passes control *back* to the
fiber that called it. It's a runtime error to try to call a fiber that is
already done.
## Yielding
@ -66,67 +75,70 @@ Things get interesting when a fiber *yields*. A yielded fiber passes control
*back* to the fiber that ran it, but *remembers where it is*. The next time the
fiber is called, it picks up right where it left off and keeps going.
You can make a fiber yield by calling the static `yield()` method on `Fiber`:
You make a fiber yield by calling the static `yield()` method on Fiber:
:::wren
var fiber = Fiber.new {
System.print("fiber 1")
Fiber.yield()
System.print("fiber 2")
}
<pre class="snippet">
var fiber = Fiber.new {
System.print("Before yield")
Fiber.yield()
System.print("Resumed")
}
System.print("main 1")
fiber.call()
System.print("main 2")
fiber.call()
System.print("main 3")
System.print("Before call") //> Before call
fiber.call() //> Before yield
System.print("Calling again") //> Calling again
fiber.call() //> Resumed
System.print("All done") //> All done
</pre>
This program prints:
:::text
main 1
fiber 1
main 2
fiber 2
main 3
Note that even though this program has *concurrency*, it's
still *deterministic*. You can reason precisely about what it's doing and
aren't at the mercy of a thread scheduler playing Russian roulette with your
code.
Note that even though this program uses *concurrency*, it is still
*deterministic*. You can reason precisely about what it's doing and aren't at
the mercy of a thread scheduler playing Russian roulette with your code.
## Passing values
Calling and yielding fibers is used for passing control, but it can also pass
*data*. When you call a fiber, you can optionally pass a value to it. If the
fiber has yielded and is waiting to resume, the value becomes the return value
of the `yield()` call:
*data*. When you call a fiber, you can optionally pass a value to it.
:::wren
var fiber = Fiber.new {
var result = Fiber.yield()
System.print(result)
}
If you create a fiber using a function that takes a parameter, you can pass a
value to it through `call()`:
fiber.call("discarded")
fiber.call("sent")
<pre class="snippet">
var fiber = Fiber.new {|param|
System.print(param)
}
This prints "sent". Note that the first value sent to the fiber through call is
ignored. That's because the fiber isn't waiting on a `yield()` call, so there's
nowhere for the sent value to go.
fiber.call("Here you go") //> Here you go
</pre>
If the fiber has yielded and is waiting to resume, the value you pass to call
becomes the return value of the `yield()` call when it resumes:
<pre class="snippet">
var fiber = Fiber.new {|param|
System.print(param)
var result = Fiber.yield()
System.print(result)
}
fiber.call("First") //> First
fiber.call("Second") //> Second
</pre>
Fibers can also pass values *back* when they yield. If you pass an argument to
`yield()`, that will become the return value of the `call()` that was used to
invoke the fiber:
:::wren
var fiber = Fiber.new {
Fiber.yield("sent")
}
<pre class="snippet">
var fiber = Fiber.new {
Fiber.yield("Reply")
}
System.print(fiber.call())
System.print(fiber.call()) //> Reply
</pre>
This also prints "sent".
This is sort of like how a function call may return a value, except that a fiber
may return a whole sequence of values, one every time it yields.
## Full coroutines
@ -135,20 +147,23 @@ Python and C# that have *generators*. Those let you define a function call that
you can suspend and resume. When using the function, it appears like a sequence
you can iterate over.
Wren's fibers can do that, but they can do much more. Like Lua, they are
full *coroutines*&mdash;they can suspend from anywhere in the callstack. For
example:
Wren's fibers can do that, but they can do much more. Like Lua, they are full
*coroutines*&mdash;they can suspend from anywhere in the callstack. The function
you use to create a fiber can call a method that calls another method that calls
some third method which finally calls yield. When that happens, *all* of those
method calls &mdash; the entire callstack &mdash; gets suspended. For example:
:::wren
var fiber = Fiber.new {
(1..10).map {|i|
Fiber.yield(i)
}
}
<pre class="snippet">
var fiber = Fiber.new {
(1..10).each {|i|
Fiber.yield(i)
}
}
</pre>
Here, we're calling `yield()` from within a [function](functions.html) being
passed to the `map()` method. This works fine in Wren because that inner
`yield()` call will suspend the call to `map()` and the function passed to it
passed to the `each()` method. This works fine in Wren because that inner
`yield()` call will suspend the call to `each()` and the function passed to it
as a callback.
## Transferring control
@ -158,15 +173,22 @@ Fibers have one more trick up their sleeves. When you execute a fiber using
lets you build up a chain of fiber calls that will eventually unwind back to
the main fiber when all of the called ones yield or finish.
This is almost always what you want. But if you're doing something really low
level, like writing your own scheduler to manage a pool of fibers, you may not
want to treat them explicitly like a stack.
This is usually what you want. But if you're doing something low level, like
writing your own scheduler to manage a pool of fibers, you may not want to treat
them explicitly like a stack.
For rare cases like that, fibers also have a `transfer()` method. This switches
execution immediately to the transferred fiber. The previous one is suspended,
leaving it in whatever state it was in. You can resume the previous fiber by
transferring back to it, or even calling it. If you don't, execution stops when
the last transferred fiber returns.
execution to the transferred fiber and "forgets" the fiber that was transferred
*from*. The previous one is suspended, leaving it in whatever state it was in.
You can resume the previous fiber by explicitly transferring back to it, or even
calling it. If you don't, execution stops when the last transferred fiber
returns.
Where `call()` and `yield()` are analogous to calling and returning from
functions, `transfer()` works more like an unstructured goto. It lets you freely
switch control between a number of fibers, all of which act as peers to one
another.
<br><hr>
<a class="right" href="error-handling.html">Error Handling &rarr;</a>
<a href="classes.html">&larr; Classes</a>

View File

@ -4,7 +4,7 @@ Like the bird, Wren's ecosystem is small but full of life. Almost everything is
under active development and there's lots to do. We'd be delighted to have you
help.
The first thing to do is to join [the official mailing list][list] and say,
The first thing to do is to join [the discord community][discord] (or [the mailing list][list]) and say,
"Hi". There are no strangers to Wren, just friends we haven't met yet.
## Growing the ecosystem
@ -14,21 +14,21 @@ The simplest and often most helpful way to join the Wren party is to be a Wren
utility in Wren. Add syntax highlighting support for Wren to your favorite text
editor. Share that stuff and it will help the next Wren user to come along.
If you do any of the above, let us know by adding it to [the wiki][wiki]. We
like to keep track of:
If you do any of the above, let us know by adding it to [the wiki][wiki].
We like to keep track of:
[wiki]: https://github.com/munificent/wren/wiki
[wiki]: https://github.com/wren-lang/wren/wiki
* [Applications][] that host Wren as a scripting language.
* [Modules][] written in Wren that others can use.
* [Language bindings][] that let you interact with Wren from other programming
* [Language bindings][] that let you interact with Wren from other
languages.
* Other [tools and utilities][] that make it easier to be a Wren programmer.
* [Tools and utilities][] that make it easier to be a Wren programmer.
[applications]: https://github.com/munificent/wren/wiki/Applications
[modules]: https://github.com/munificent/wren/wiki/Modules
[language bindings]: https://github.com/munificent/wren/wiki/Language-Bindings
[tools and utilities]: https://github.com/munificent/wren/wiki/Tools
[applications]: https://github.com/wren-lang/wren/wiki/Applications
[modules]: https://github.com/wren-lang/wren/wiki/Modules
[language bindings]: https://github.com/wren-lang/wren/wiki/Language-Bindings
[tools and utilities]: https://github.com/wren-lang/wren/wiki/Tools
## Contributing to Wren
@ -37,6 +37,8 @@ the command-line interpreter. The source is developed [on GitHub][github]. Our
hope is that the codebase, tests, and [documentation][docs] are easy to
understand and contribute to. If they aren't, that's a bug.
You can learn how to build wren on the [getting started page](getting-started.html#building-wren).
### Finding something to hack on
Between the [issue tracker][issue] and searching for `TODO` comments in the
@ -52,41 +54,25 @@ means often having to say "no" to language additions, even really cool ones.
The [documentation][] is one of the easiest&mdash;and most
important!&mdash;parts of Wren to contribute to. The source for the site is
written in [Markdown][] (and a little [SASS][]) and lives under `doc/site`. A
simple Python script, `util/generate_docs.py`, converts that to HTML and CSS.
written in [Markdown][] and lives under `doc/site`. A
simple Python 3 script, `util/generate_docs.py`, converts that to HTML and CSS.
[documentation]: /
[markdown]: http://daringfireball.net/projects/markdown/
[sass]: http://sass-lang.com/
The site uses [Pygments][] for syntax highlighting, with a custom lexer plug-in
for Wren. To install that, run:
[pygments]: http://pygments.org
:::sh
$ cd util/pygments-lexer
$ sudo python setup.py develop
$ cd ../.. # Back to the root Wren directory.
Now you can build the docs:
:::sh
$ make docs
$ python util/generate_docs.py
This generates the site in `build/docs/`. You can run any simple static web
server from there. Python includes one:
:::sh
$ cd build/docs
$ python -m SimpleHTTPServer
$ python -m http.server
Running `make docs` is a drag every time you change a line of Markdown or SASS,
Running that script every time you change a line of Markdown can be slow,
so there is also a file watching version that will automatically regenerate the
docs when you edit a file:
:::sh
$ make watchdocs
$ python util/generate_docs.py --watch
### Hacking on the VM
@ -94,21 +80,20 @@ The basic process is simple:
1. **Make sure you can build and run the tests locally.** It's good to ensure
you're starting from a happy place before you poke at the code. Running the
tests is as simple as:
tests is as simple as [building the vm project](getting-started.html#building-wren),
which generates `bin/wren_test` and then running the following python 3 script:
:::sh
$ make test
$ python util/test.py
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
columns, and two spaces of indentation. If you see places where the existing
code is inconsistent, let me know.
code is inconsistent, let us know.
4. **Write some tests for your new functionality.** They live under `test/`.
Take a look at some existing tests to get an idea of how to define
@ -119,24 +104,24 @@ 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
If at any point you have questions, feel free to [file an issue][issue] or ask
on the [mailing list][list]. If you're a Redditor, try the
on the [discord community][discord] (or the [mailing list][list]). If you're a Redditor, try the
[/r/wren_lang][subreddit] subreddit. You can also email me directly (`robert` at
`stuffwithstuff.com`) if you want something less public.
[mit]: http://opensource.org/licenses/MIT
[github]: https://github.com/munificent/wren
[github]: https://github.com/wren-lang/
[fork]: https://help.github.com/articles/fork-a-repo/
[docs]: https://github.com/munificent/wren/tree/master/doc/site
[issue]: https://github.com/munificent/wren/issues
[proposal]: https://github.com/munificent/wren/labels/proposal
[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/munificent/wren/tree/master/AUTHORS
[pull request]: https://github.com/munificent/wren/pulls
[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/
[discord]: https://discord.gg/Kx6PxSX

View File

@ -29,39 +29,43 @@ values.
The simplest branching statement, `if` lets you conditionally skip a chunk of
code. It looks like this:
:::wren
if (ready) System.print("go!")
<pre class="snippet">
if (ready) System.print("go!")
</pre>
That evaluates the parenthesized expression after `if`. If it's true, then the
statement after the condition is evaluated. Otherwise it is skipped. Instead of
a statement, you can have a [block](syntax.html#blocks):
:::wren
if (ready) {
System.print("getSet")
System.print("go!")
}
<pre class="snippet">
if (ready) {
System.print("getSet")
System.print("go!")
}
</pre>
You may also provide an `else` branch. It will be executed if the condition is
false:
:::wren
if (ready) System.print("go!") else System.print("not ready!")
<pre class="snippet">
if (ready) System.print("go!") else System.print("not ready!")
</pre>
And, of course, it can take a block too:
:::wren
if (ready) {
System.print("go!")
} else {
System.print("not ready!")
}
<pre class="snippet">
if (ready) {
System.print("go!")
} else {
System.print("not ready!")
}
</pre>
## Logical operators
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&mdash;they short-circuit.
only conditionally evaluate the right operand&mdash;they short-circuit.
[operators]: method-calls.html#operators
[method calls]: method-calls.html
@ -70,26 +74,29 @@ A `&&` ("logical and") expression evaluates the left-hand argument. If it's
false, it returns that value. Otherwise it evaluates and returns the right-hand
argument.
:::wren
System.print(false && 1) //> false
System.print(1 && 2) //> 2
<pre class="snippet">
System.print(false && 1) //> false
System.print(1 && 2) //> 2
</pre>
A `||` ("logical or") expression is reversed. If the left-hand argument is
*true*, it's returned, otherwise the right-hand argument is evaluated and
returned:
:::wren
System.print(false || 1) //> 1
System.print(1 || 2) //> 1
<pre class="snippet">
System.print(false || 1) //> 1
System.print(1 || 2) //> 1
</pre>
## The conditional operator `?:`
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.
:::wren
System.print(1 != 2 ? "math is sane" : "math is not sane!")
<pre class="snippet">
System.print(1 != 2 ? "math is sane" : "math is not sane!")
</pre>
It takes a condition expression, followed by `?`, followed by a then
expression, a `:`, then an else expression. Just like `if`, it evaluates the
@ -105,16 +112,17 @@ they should be familiar if you've used other imperative languages.
The simplest, a `while` statement executes a chunk of code as long as a
condition continues to hold. For example:
:::wren
// Hailstone sequence.
var n = 27
while (n != 1) {
if (n % 2 == 0) {
n = n / 2
} else {
n = 3 * n + 1
}
}
<pre class="snippet">
// Hailstone sequence.
var n = 27
while (n != 1) {
if (n % 2 == 0) {
n = n / 2
} else {
n = 3 * n + 1
}
}
</pre>
This evaluates the expression `n != 1`. If it is true, then it executes the
following body. After that, it loops back to the top, and evaluates the
@ -125,9 +133,10 @@ The condition for a while loop can be any expression, and must be surrounded by
parentheses. The body of the loop is usually a curly block but can also be a
single statement:
:::wren
var n = 27
while (n != 1) if (n % 2 == 0) n = n / 2 else n = 3 * n + 1
<pre class="snippet">
var n = 27
while (n != 1) if (n % 2 == 0) n = n / 2 else n = 3 * n + 1
</pre>
## For statements
@ -136,10 +145,11 @@ some complex condition. But in most cases, you're looping through
a [list](lists.html), a series of numbers, or some other "sequence" object.
That's what `for` is, uh, for. It looks like this:
:::wren
for (beatle in ["george", "john", "paul", "ringo"]) {
System.print(beatle)
}
<pre class="snippet">
for (beatle in ["george", "john", "paul", "ringo"]) {
System.print(beatle)
}
</pre>
A `for` loop has three components:
@ -160,11 +170,27 @@ and stop. To do that, you can use a `break` statement. It's just the `break`
keyword all by itself. That immediately exits out of the nearest enclosing
`while` or `for` loop.
:::wren
for (i in [1, 2, 3, 4]) {
System.print(i) //> 1
if (i == 3) break //> 2
} //> 3
<pre class="snippet">
for (i in [1, 2, 3, 4]) {
System.print(i) //> 1
if (i == 3) break //> 2
} //> 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]) {
if (i == 2) continue //> 1
System.print(i) //> 3
} //> 4
</pre>
## Numeric ranges
@ -172,18 +198,20 @@ Lists are one common use for `for` loops, but sometimes you want to walk over a
sequence of numbers, or loop a number of times. For that, you can create a
[range](values.html#ranges), like so:
:::wren
for (i in 1..100) {
System.print(i)
}
<pre class="snippet">
for (i in 1..100) {
System.print(i)
}
</pre>
This loops over the numbers from 1 to 100, including 100 itself. If you want to
leave off the last value, use three dots instead of two:
:::wren
for (i in 1...100) {
System.print(i)
}
<pre class="snippet">
for (i in 1...100) {
System.print(i)
}
</pre>
This looks like some special "range" syntax in the `for` loop, but it's actually
just a pair of operators. The `..` and `...` syntax are infix "range" operators.
@ -203,20 +231,22 @@ methods on the object that resulted from evaluating the sequence expression.
When you write a loop like this:
:::wren
for (i in 1..100) {
System.print(i)
}
<pre class="snippet">
for (i in 1..100) {
System.print(i)
}
</pre>
Wren sees it something like this:
:::wren
var iter_ = null
var seq_ = 1..100
while (iter_ = seq_.iterate(iter_)) {
var i = seq_.iteratorValue(iter_)
System.print(i)
}
<pre class="snippet">
var iter_ = null
var seq_ = 1..100
while (iter_ = seq_.iterate(iter_)) {
var i = seq_.iteratorValue(iter_)
System.print(i)
}
</pre>
First, Wren evaluates the sequence expression and stores it in a hidden
variable (written `seq_` in the example but in reality it doesn't have a name
@ -240,5 +270,6 @@ The built-in [List](lists.html) and [Range](values.html#ranges) types implement
`iterate()` and `iteratorValue()` to walk over their respective sequences. You
can implement the same methods in your classes to make your own types iterable.
<br><hr>
<a class="right" href="variables.html">Variables &rarr;</a>
<a href="method-calls.html">&larr; Method Calls</a>

View File

@ -1,9 +0,0 @@
^title Application Lifecycle
**TODO: Write these docs.**
Until these are written, you can read the docs in [wren.h][].
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
<a href="configuring-the-vm.html">&larr; Configuring the VM</a>

View File

@ -1,10 +1,142 @@
^title Calling C from Wren
**TODO: Write these docs.**
When we are ensconced within the world of Wren, the external C world is
"foreign" to us. There are two reasons we might want to bring some foreign
flavor into our VM:
Until these are written, you can read the docs in [wren.h][].
* We want to execute code written in C.
* We want to store raw C data.
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
Since Wren is object-oriented, behavior lives in methods, so for the former we
have **foreign methods**. Likewise, data lives in objects, so for the latter, we
define **foreign classes**. This page is about the first, foreign methods. The
[next page][] covers foreign classes.
[next page]: /embedding/storing-c-data.html
A foreign method looks to Wren like a regular method. It is defined on a Wren
class, it has a name and signature, and calls to it are dynamically dispatched.
The only difference is that the *body* of the method is written in C.
A foreign method is declared in Wren like so:
<pre class="snippet">
class Math {
foreign static add(a, b)
}
</pre>
The `foreign` keyword tells Wren that the method `add()` is declared on `Math`,
but implemented in C. Both static and instance methods can be foreign.
## Binding Foreign Methods
When you call a foreign method, Wren needs to figure out which C function to
execute. This process is called *binding*. Binding is performed on-demand by the
VM. When a class that declares a foreign method is executed -- when the `class`
statement itself is evaluated -- the VM asks the host application for the C
function that should be used for the foreign method.
It does this through the `bindForeignMethodFn` callback you give it when you
first [configure the VM][config]. This callback isn't the foreign method itself.
It's the binding function your app uses to *look up* foreign methods.
[config]: configuring-the-vm.html
Its signature is:
<pre class="snippet" data-lang="c">
WrenForeignMethodFn bindForeignMethodFn(
WrenVM* vm,
const char* module,
const char* className,
bool isStatic,
const char* signature);
</pre>
Every time a foreign method is first declared, the VM invokes this callback. It
passes in the module containing the class declaration, the name of the class
containing the method, the method's signature, and whether or not it's a static
method. In the above example, it would pass something like:
<pre class="snippet" data-lang="c">
bindForeignMethodFn(vm, "main", "Math", true, "add(_,_)");
</pre>
When you configure the VM, you give it a C callback that looks up the
appropriate function for the given foreign method and returns a pointer to it.
Something like:
<pre class="snippet" data-lang="c">
WrenForeignMethodFn bindForeignMethod(
WrenVM* vm,
const char* module,
const char* className,
bool isStatic,
const char* signature)
{
if (strcmp(module, "main") == 0)
{
if (strcmp(className, "Math") == 0)
{
if (isStatic && strcmp(signature, "add(_,_)") == 0)
{
return mathAdd; // C function for Math.add(_,_).
}
// Other foreign methods on Math...
}
// Other classes in main...
}
// Other modules...
}
</pre>
This implementation is pretty tedious, but you get the idea. Feel free to do
something more clever here in your host application.
The important part is that it returns a pointer to a C function to use for that
foreign method. Wren does this binding step *once* when the class definition is
first executed. It then keeps the function pointer you return and associates it
with that method. This way, *calls* to the foreign method are fast.
## Implementing a Foreign Method
All C functions for foreign methods have the same signature:
<pre class="snippet" data-lang="c">
void foreignMethod(WrenVM* vm);
</pre>
Arguments passed from Wren are not passed as C arguments, and the method's
return value is not a C return value. Instead -- you guessed it -- we go through
the [slot array][].
[slot array]: /embedding/slots-and-handles.html
When a foreign method is called from Wren, the VM sets up the slot array with
the receiver and arguments to the call. As in calling Wren from C, the receiver
object is in slot zero, and arguments are in consecutive slots after that.
You use the slot API to read those arguments, and then perform whatever work you
want to in C. If you want the foreign method to return a value, place it in slot
zero. Like so:
<pre class="snippet" data-lang="c">
void mathAdd(WrenVM* vm)
{
double a = wrenGetSlotDouble(vm, 1);
double b = wrenGetSlotDouble(vm, 2);
wrenSetSlotDouble(vm, 0, a + b);
}
</pre>
While your foreign method is executing, the VM is completely suspended. No other
fibers run until your foreign method returns. You should *not* try to resume the
VM from within a foreign method by calling `wrenCall()` or `wrenInterpret()`.
The VM is not re-entrant.
This covers foreign behavior, but what about foreign *state*? For that, we need
a foreign *class*...
<a class="right" href="storing-c-data.html">Storing C Data &rarr;</a>
<a href="calling-wren-from-c.html">&larr; Calling Wren from C</a>

View File

@ -1,129 +1,191 @@
^title Calling Wren from C
From C, we can tell Wren to do stuff by calling `wrenInterpret()`, but that's not always the ideal way to drive the VM. First of all, it's slow. It has to parse and compile the string of source code you give it. Wren has a pretty fast compiler, but that's still a good bit of work.
From C, we can tell Wren to do stuff by calling `wrenInterpret()`, but that's
not always the ideal way to drive the VM. First of all, it's slow. It has to
parse and compile the string of source code you give it. Wren has a pretty fast
compiler, but that's still a good bit of work.
It's also not an effective way to communicate. You can't pass arguments to Wren&mdash;at least, not without doing something nasty like converting them to literals in a string of source code&mdash;and we can get a result value back.
It's also not an effective way to communicate. You can't pass arguments to
Wren&mdash;at least, not without doing something nasty like converting them to
literals in a string of source code&mdash;and you can't get a result value back.
`wrenInterpret()` is great for loading some new code into the VM. But it's not the best way to execute code that's already been loaded. What we want to do is invoke some already compiled chunk of code. Since Wren is an object-oriented language, "chunk of code" means a [method][], not a [function][].
`wrenInterpret()` is great for loading code into the VM, but it's not the best
way to execute code that's already been loaded. What we want to do is invoke
some already compiled chunk of code. Since Wren is an object-oriented language,
"chunk of code" means a [method][], not a [function][].
[method]: ../method-calls.html
[function]: ../functions.html
The C API for doing this is `wrenCall()`. In order to invoke a Wren method from C, first we need a way to refer to the method itself.
The C API for doing this is `wrenCall()`. In order to invoke a Wren method from
C, we need a few things:
* **The method to call.** Wren is dynamically typed, so this means we'll look it up by name. Further, since Wren supports overloading by arity, we actually need its entire [signature][].
* **The method to call.** Wren is dynamically typed, so this means we'll look it
up by name. Further, since Wren supports overloading by arity, we actually
need its entire [signature][].
[signature]: ../method-calls.html#signature
* **The receiver to invoke the method on.** It's the receiver's class that
determines which method is actually called.
* **The receiver object to invoke the method on.** The receiver's class
determines which method is actually called.
* **The arguments to pass to the method.**
We'll tackle these one at a time.
### Getting a method handle
### Getting a Method Handle
When you run a chunk of Wren code like this:
:::wren
object.someMethod(1, 2, 3)
<pre class="snippet">
object.someMethod(1, 2, 3)
</pre>
At runtime, the VM has to look up the class of `object` and find a method there whose signature is `someMethod(_,_,_)`. This sounds like it's doing some string manipulation&mdash;at the very least hashing the signature&mdash;every time a method is called. That's how many dynamic languages work.
At runtime, the VM has to look up the class of `object` and find a method there
whose signature is `someMethod(_,_,_)`. This sounds like it's doing some string
manipulation&mdash;at the very least hashing the signature&mdash;every time a
method is called. That's how many dynamic languages work.
But, as you can imagine, that's pretty slow. So, instead, Wren does as much of that work at compile time as it can. When it's compiling the above code to bytecode, it takes that method signature a converts it to a *method symbol*, a number that uniquely identifes that method. That's the only part of the process that requires treating a signature as a string.
But, as you can imagine, that's pretty slow. So, instead, Wren does as much of
that work at compile time as it can. When it's compiling the above code to
bytecode, it takes that method signature a converts it to a *method symbol*, a
number that uniquely identifes that method. That's the only part of the process
that requires treating a signature as a string.
At runtime, the VM just looks for the method *symbol* in the receiver's class's method table. In fact, the way it's implemented today, the symbol is simply the array index into the table. That's why method calls are so fast in Wren.
At runtime, the VM just looks for the method *symbol* in the receiver's class's
method table. In fact, the way it's implemented today, the symbol is simply the
array index into the table. That's [why method calls are so fast][perf] in Wren.
It would be a shame if calling a method from C didn't have that same speed benefit. So to do that, we split the process of calling a method into two steps.
[perf]: ../performance.html
First, we create a handle that represents a "compiled" method signature. You do that using this:
It would be a shame if calling a method from C didn't have that same speed
benefit. To achieve that, we split the process of calling a method into two
steps. First, we create a handle that represents a "compiled" method signature:
:::c
WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
<pre class="snippet" data-lang="c">
WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
</pre>
That takes a method signature as a string and gives you back an opaque handle that represents the compiled method symbol. Now you have a *reusable* handle that can be used to very quickly call a certain method given a receiver and some arguments.
That takes a method signature as a string and gives you back an opaque handle
that represents the compiled method symbol. Now you have a *reusable* handle
that can be used to very quickly call a certain method given a receiver and some
arguments.
This is just a regular WrenHandle, which means you can hold onto it as long as
you like. Typically, you'd call this once outside of your applications
performance critical loops and reuse it as long as you need. Then, it is us up
to you to release it when you no longer need it by calling
`wrenReleaseHandle()`.
you like. Typically, you'd call this once outside of your application's
performance critical loops and reuse it as long as you need. It is us up to you
to release it when you no longer need it by calling `wrenReleaseHandle()`.
### Setting up a receiver
## Setting Up a Receiver
OK, we have a method, but who are we calling it on? We need a receiver, and as you can probably guess after reading the [last section][], we give that to Wren by storing it in a slot. In particular, **the receiver for a method call goes in slot zero.**
OK, we have a method, but who are we calling it on? We need a receiver, and as
you can probably guess after reading the [last section][], we give that to Wren
by storing it in a slot. In particular, **the receiver for a method call goes in
slot zero.**
Any object you store in that slot can be used as a receiver. You could even call `+` on a number by storing a number in there if you felt like it.
Any object you store in that slot can be used as a receiver. You could even call
`+` on a number by storing a number in there if you felt like it.
[last section]: slots-and-handles.html
*Needing* to pick some kind of receiver from C might feel strange. C is procedural, so it's natural to want to just invoke a bare *function* from Wren, but Wren isn't procedural. Instead, if you want to define some executable operation that isn't logically tied to a specific object, the natural way is to define a static method on an appropriate class.
Needing a receiver to call some Wren code from C might feel strange. C is
procedural, so it's natural to want to just invoke a bare *function* from Wren,
but Wren isn't procedural. Instead, if you want to define some executable
operation that isn't logically tied to a specific object, the natural way is to
define a static method on an appropriate class.
For example, say we're making a game engine. From C, we want to tell the game engine to update all of the entities each frame. We'll keep track of the list of entities within Wren, so from C, there's no obvious object to call `update(_)` on. Instead, we'll just make it a static method:
For example, say we're making a game engine. From C, we want to tell the game
engine to update all of the entities each frame. We'll keep track of the list of
entities within Wren, so from C, there's no obvious object to call `update(_)`
on. Instead, we'll just make it a static method:
:::wren
class GameEngine {
static update(elapsedTime) {
// ...
}
}
<pre class="snippet">
class GameEngine {
static update(elapsedTime) {
// ...
}
}
</pre>
So, very often, when you call a method from C, you'll be calling a static method. But, even then, you need a receiver. Now, though, the receiver is the *class itself*. Classes are first class objects in Wren, and when you define a named class, you're really declaring a variable with the class's name and storing a reference to the class object in it.
Often, when you call a Wren method from C, you'll be calling a static method.
But, even then, you need a receiver. Now, though, the receiver is the *class
itself*. Classes are first class objects in Wren, and when you define a named
class, you're really declaring a variable with the class's name and storing a
reference to the class object in it.
Assuming you declared that class at the top level, the C API [gives you a way to look it up][variable].
Assuming you declared that class at the top level, the C API [gives you a way to
look it up][variable]. We can get a handle to the above class like so:
[variable]: slots-and-handles.html#looking-up-variables
We can get a handle to the above class like so:
<pre class="snippet" data-lang="c">
// Load the class into slot 0.
wrenEnsureSlots(vm, 1);
wrenGetVariable(vm, "main", "GameEngine", 0);
</pre>
:::c
// Load the class into slot 0.
wrenEnsureSlots(vm, 1);
wrenGetVariable(vm, "main", "GameEngine", 0);
We could do this every time we call `update()`, but, again, that's kind of slow
because we're looking up "GameEngine" by name each time. A faster solution is to
create a handle to the class once and use it each time:
We could do this every time we call `update()`, but, again, that's kind of slow because we're looking up "GameEngine" by name each time. A faster solution is to create a handle to the class once and use it each time:
<pre class="snippet" data-lang="c">
// Load the class into slot 0.
wrenEnsureSlots(vm, 1);
wrenGetVariable(vm, "main", "GameEngine", 0);
WrenHandle* gameEngineClass = wrenGetSlotHandle(vm, 0);
</pre>
:::c
// Load the class into slot 0.
wrenEnsureSlots(vm, 1);
wrenGetVariable(vm, "main", "GameEngine", 0);
WrenHandle* gameEngine = wrenGetSlotHandle(vm, 0);
Now, each time we want to call a method on GameEngine, we store that value back
in slot zero:
Now, each time we want to call a method on GameEngine, we store that value back in slot zero:
<pre class="snippet" data-lang="c">
wrenSetSlotHandle(vm, 0, gameEngineClass);
</pre>
:::c
wrenSetSlotHandle(vm, 0, gameEngine);
Just like we hoisted `wrenMakeCallHandle()` out of our performance critical
loop, we can hoist the call to `wrenGetVariable()` out. Of course, if your code
isn't performance critical, you don't have to do this.
Just like we hoisted `wrenMakeCallHandle()` out of our performance critical loop, we can hoist the call to `wrenGetVariable()` out. Of course, if your code isn't performance critical, you don't have to do this.
## Passing Arguments
### Passing arguments
We've got a receiver in slot zero now, next we need to pass in any other
arguments. In our GameEngine example, that's just the elapsed time. Method
arguments go in consecutive slots after the receiver. So the elapsed time goes
into slot one. You can use any of the slot functions to set this up. For the
example, it's just:
We've got a receiver in slot zero now, next we need to pass in any other arguments. In our GameEngine example, that's just the elapsed time. The remaining arguments go in adjacent slots, right after the receiver. So our one elapsed time goes into slot one. You can use any of the slot functions to set this up. For the example, it's just:
<pre class="snippet" data-lang="c">
wrenSetSlotDouble(vm, 1, elapsedTime);
</pre>
:::c
wrenSetSlotDouble(vm, 1, elapsedTime);
## Calling the Method
### Calling the method
We have all of the data in place, so all that's left is to pull the trigger and
tell the VM to start running some code:
We have all of the data in place, so all that's left is to pull the trigger and tell the VM to start running some code. There's one more function to call:
<pre class="snippet" data-lang="c">
WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
</pre>
:::c
WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
It takes the method handle we created using `wrenMakeCallHandle()`. Now Wren
starts running code. It looks up the method on the receiver, executes it and
keeps running until either the method returns or a fiber [suspends][].
It takes the method handle we created using `wrenMakeCallHandle()`. It assumes you have already set up the receiver and arguments in the slot array. Critically, it assumes you have as many arguments as the method signature defines. If you call a method like `takeThree(_,_,_)` and don't put three arguments in the slot array, bad things we'll happen.
[suspends]: ../modules/core/fiber.html#fiber.suspend()
Now Wren starts running code. It looks up the method on the receiver, executes it and keeps running until either the method returns or a fiber [suspends][].
`wrenCall()` returns the same WrenInterpretResult enum as `wrenInterpret()` to
tell you if the method completed successfully or a runtime error occurred.
(`wrenCall()` never returns `WREN_ERROR_COMPILE` since it doesn't compile
anything.)
[suspend]: ../modules/core/fiber.html#fiber.suspend()
## Getting the Return Value
`wrenCall()` returns the same WrenInterpretResult enum to tell you if the method completed successfully or a runtime error occurred. You'll never get a compile error from `wrenCall()`.
When `wrenCall()` returns, it leaves the slot array in place. In slot zero, you
can find the method's return value, which you can access using any of the slot
reading functions. If you don't need the return value, you can ignore it.
### Getting the return value
When `wrenCall()` returns, it leaves the slot array in place. In slot zero, you can find the method's return value, which you can access using any of the slot reading functions. If you don't need the return value, you can ignore it.
This is how you drive Wren from C, but how do you put control in Wren's hands? For that, you'll need the next section...
This is how you drive Wren from C, but how do you put control in Wren's hands?
For that, you'll need the next section...
<a class="right" href="calling-c-from-wren.html">Calling C From Wren &rarr;</a>
<a href="slots-and-handles.html">&larr; Slots and Handles</a>

View File

@ -1,10 +1,238 @@
^title Configuring the VM
**TODO: Write these docs.**
When you create a Wren VM, you tweak it by passing in a pointer to a
WrenConfiguration structure. Since Wren has no global state, you can configure
each VM differently if your application happens to run multiple.
Until these are written, you can read the docs in [wren.h][].
The struct looks like:
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
<pre class="snippet" data-lang="c">
typedef struct
{
WrenReallocateFn reallocateFn;
WrenLoadModuleFn loadModuleFn;
WrenBindForeignMethodFn bindForeignMethodFn;
WrenBindForeignClassFn bindForeignClassFn;
WrenWriteFn writeFn;
WrenErrorFn errorFn;
size_t initialHeapSize;
size_t minHeapSize;
int heapGrowthPercent;
} WrenConfiguration;
</pre>
Most fields have useful defaults, which you can (and should) initialize by
calling:
<pre class="snippet" data-lang="c">
wrenInitConfiguration(&configuration);
</pre>
Calling this ensures that your VM doesn't get uninitialized configuration when
new fields are added to WrenConfiguration. Here is what each field does, roughly
categorized:
## Binding
The VM is isolated from the outside world. These callbacks let the VM request
access to imported code and foreign functionality.
### **`loadModuleFn`**
This is the callback Wren uses to load an imported module. The VM itself does
not know how to talk to the file system, so when an `import` statement is
executed, it relies on the host application to locate and read the source code
for a module.
The signature of this function is:
<pre class="snippet" data-lang="c">
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 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
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 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`**
The callback Wren uses to find a foreign method and bind it to a class. See
[this page][foreign method] for details. If your application defines no foreign
methods, you can leave this `NULL`.
[foreign method]: /embedding/calling-c-from-wren.html
### **`bindForeignClassFn`**
The callback Wren uses to find a foreign class and get its foreign methods. See
[this page][foreign class] for details. If your application defines no foreign
classes, you can leave this `NULL`.
[foreign class]: /embedding/storing-c-data.html
## Diagnostics
These let you wire up some minimal output so you can tell if your code is doing
what you expect.
### **`writeFn`**
This is the callback Wren uses to output text when `System.print()` or the other
related functions are called. This is the minimal connection the VM has with the
outside world and lets you do rudimentary "printf debugging". Its signature is:
<pre class="snippet" data-lang="c">
void write(WrenVM* vm, const char* text)
</pre>
Wren does *not* have a default implementation for this. It's up to you to wire
it up to `printf()` or some other way to show the text. If you leave it `NULL`,
calls to `System.print()` and others silently do nothing.
### **`errorFn`**
Wren uses this callback to report compile time and runtime errors. Its signature
is:
<pre class="snippet" data-lang="c">
void error(
WrenVM* vm,
WrenErrorType type,
const char* module,
int line,
const char* message)
</pre>
The `type` parameter is one of:
<pre class="snippet" data-lang="c">
typedef enum
{
// A syntax or resolution error detected at compile time.
WREN_ERROR_COMPILE,
// The error message for a runtime error.
WREN_ERROR_RUNTIME,
// One entry of a runtime error's stack trace.
WREN_ERROR_STACK_TRACE
} WrenErrorType;
</pre>
When a compile error occurs, `errorFn` is called once with type
`WREN_ERROR_COMPILE`, the name of the module and line where the error occurs,
and the error message.
Runtime errors include stack traces. To handle this, Wren first calls `errorFn`
with `WREN_ERROR_RUNTIME`, no module or line, and the runtime error's message.
After that, it calls `errorFn` again using type `WREN_ERROR_STACK_TRACE`, once
for each line in the stack trace. Each of those calls has the module and line
where the method or function is defined and `message` is the name of the method
or function.
If you leave this `NULL`, Wren does not report any errors.
## Memory Management
These fields control how the VM allocates and manages memory.
### **`reallocateFn`**
This lets you provide a custom memory allocation function. Its signature is:
<pre class="snippet" data-lang="c">
void* reallocate(void* memory, size_t newSize, void* userData)
</pre>
Wren uses this one function to allocate, grow, shrink, and deallocate memory.
When called, `memory` is the existing pointer to the block of memory if an
allocation is being changed or freed. If Wren is requesting new memory, then
`memory` is `NULL`.
`newSize` is the number of bytes of memory being requested. If memory is being
freed, this is zero. Your callback should allocate the proper amount of memory
and return it.
If you don't provide a custom allocator, the VM uses a default one that relies
on `realloc` and `free`.
### **`initialHeapSize`**
This defines the total number of bytes of memory the VM will allocate before
triggering the first garbage collection. Setting this to a smaller number
reduces the amount of memory Wren will have allocated at one time, but causes it
to collect garbage more frequently.
If you set this to zero, Wren uses a default size of 10MB.
### **`minHeapSize`**
After a garbage collection occurs, the threshold for the *next* collection is
determined based on the number of bytes remaining in use. This allows Wren to
grow or shrink its memory usage automatically based on how much memory is
actually needed.
This can be used to ensure that the heap does not get *too* small, which can
in turn lead to a large number of collections afterwards as the heap grows
back to a usable size.
If zero, this defaults to 1MB.
### **`heapGrowthPercent`**
Wren tunes the rate of garbage collection based on how much memory is still in
use after a collection. This number controls that. It determines the amount of
additional memory Wren will use after a collection, as a percentage of the
current heap size.
For example, say that this is 50. After a garbage collection, there are 400
bytes of memory still in use. That means the next collection will be triggered
after a total of 600 bytes are allocated (including the 400 already in use.)
Setting this to a smaller number wastes less memory, but triggers more
frequent garbage collections.
If set to zero, the VM uses a default of 50.
<a class="right" href="application-lifecycle.html">Application Lifecycle &rarr;</a>
<a href="storing-c-data.html">&larr; Storing C Data</a>

View File

@ -7,20 +7,19 @@ features. Designing this API well requires satisfying several constraints:
1. **Wren is dynamically typed, but C is not.** A variable can hold a value of
any type in Wren, but that's definitely not the case in C unless you define
some sort of variant type, which ultimately just kicks the problem down the
road. Eventually, we have to move data across the boundary between typed and
untyped code.
road. Eventually, we have to move data across the boundary between statically and dynamically typed code.
2. **Wren uses garbage collection, but C manages memory manually.** GC adds a
few constraints on the API. The VM must be able to find every Wren object
that is still usable, even if that object is being held onto by native C
code. Otherwise, it could free an object that's still in use.
that is still usable, even if that object is being referenced from native C
code. Otherwise, Wren could free an object that's still in use.
Also, we ideally don't want to let native C code see a bare pointer to a
chunk of memory managed by Wren. Many garbage collection strategies involve
[moving objects][] in memory. If we allow C code to point directly to an
object, that pointer will be left dangling when the object moves. To prevent
that, we can't using moving GCs even though they have some nice performance
properties.
object, that pointer will be left dangling when the object moves. Wren's GC
doesn't move objects today, but we would like to keep that option for the
future.
3. **The embedding API needs to be fast.** Users may add layers of abstraction
on top of the API to make it more pleasant to work with, but the base API
@ -29,29 +28,29 @@ features. Designing this API well requires satisfying several constraints:
it's too slow. There is no lower level alternative.
4. **We want the API to be pleasant to use.** This is the last constraint
because it's the softest. Of course, we want a beautiful, usable, API. But we
because it's the softest. Of course, we want a beautiful, usable API. But we
really *need* to handle the above, so we're willing to make things a bit more
of a chore to reach the first three goals.
[moving objects]: https://en.wikipedia.org/wiki/Tracing_garbage_collection#Copying_vs._mark-and-sweep_vs._mark-and-don.27t-sweep
Fortunately, Wren isn't the first language to tackle this. If you're familiar
with [Lua's C API][lua], you'll find Wren's to be fairly familiar.
Fortunately, we aren't the first people to tackle this. If you're familiar with
[Lua's C API][lua], you'll find Wren's similar.
[lua]: https://www.lua.org/pil/24.html
### Performance and safety
When code is safely snuggled within the confines of the VM, it's pretty safe.
Method calls are dynamically checked and generate a runtime error which can be
Method calls are dynamically checked and generate runtime errors which can be
caught and handled. The stack grows if it gets close to overflowing. In general,
when you're within Wren code, it tries very hard to avoid crashing and burning.
This is why you use a high level language after all&mdash;it's safer and more
productive than C. C, meanwhile, really assumes you know what you're doing. You
can cast pointers in invalid ways, misinterpret bits, use memory after freeing
it, etc. What that gives you in return is blazing performance. Many of the
reasons C is fast are because it takes all the governors and guardrails off.
it, etc. What you get in return is blazing performance. Many of the reasons C is
fast are because it takes all the governors and guardrails off.
Wren's embedding API defines the border between those worlds, and takes on some
of the characteristics of C. When you call any of the embedding API functions,
@ -78,93 +77,120 @@ There are two (well, three) ways to get the Wren VM into your program:
[build]: ../getting-started.html
In either case, you also want to add `src/include` to your include path so you
can get to the [public header for Wren][wren.h]:
can find the [public header for Wren][wren.h]:
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
[wren.h]: https://github.com/wren-lang/wren/blob/main/src/include/wren.h
:::c
#include "wren.h"
<pre class="snippet" data-lang="c">
#include "wren.h"
</pre>
Wren depends only on the C standard library, so you don't usually need to link
to anything else. On some platforms (at least BSD and Linux) some of the math
functions in `math.h` are implemented in a separate library, [libm][], that you
have to explicitly link to.
[libm]: https://en.wikipedia.org/wiki/C_mathematical_functions#libm
If your program is in C++ but you are linking to the Wren library compiled as C,
then you'll need to handle the calling convention differences like so:
this header handles the differences in calling conventions between C and C++:
:::c
extern "C" {
#include "wren.h"
}
(Wren's source can be compiled as either C or C++, so you can avoid this by
compiling the whole thing yourself as C++. Whatever floats your boat.)
<pre class="snippet" data-lang="c">
#include "wren.hpp"
</pre>
## 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 WrenConfiguration:
virtual machine. To do that, you create a `WrenConfiguration` object and
initialize it.
:::c
<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.
[configuration]: configuration.html
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
With this ready, you can create the VM:
:::c
WrenVM* vm = wrenNewVM(&config);
<pre class="snippet" data-lang="c">
WrenVM* vm = wrenNewVM(&config);
</pre>
This allocates memory for a new VM and initializes it. The Wren C implementation
has no global state, so every single bit of data Wren uses is bundled up inside
a WrenVM. You can have multiple Wren VMs running independently of each other
without any problems.
without any problems, even concurrently on different threads.
`wrenNewVM()` stores its own copy of the configuration, so after calling it, you
can discard the `WrenConfiguration` struct you filled in. Now you have a live
can discard the WrenConfiguration struct you filled in. Now you have a live
VM, waiting to run some code!
## Executing Wren code
You can tell the VM to execute a string of Wren source code like so:
You execute a string of Wren source code like so:
:::c
WrenInterpretResult result = wrenInterpret(vm,
"System.print(\"I am running in a VM!\")");
<pre class="snippet" data-lang="c">
WrenInterpretResult result = wrenInterpret(
vm,
"my_module",
"System.print(\"I am running in a VM!\")");
</pre>
The string is a chunk of Wren code to execute&mdash;a series of one or more
statements separated by newlines. Wren copies the string, so you can free it
after calling this.
When you call `wrenInterpret()`, Wren first compiles your source to bytecode. If
an error occurs, it returns immediately with `WREN_RESULT_COMPILE_ERROR`.
The string is a series of one or more statements separated by newlines. Wren
copies the string, so you can free it after calling this. When you call
`wrenInterpret()`, Wren first compiles your source to bytecode. If an error
occurs, it returns immediately with `WREN_RESULT_COMPILE_ERROR`.
Otherwise, Wren spins up a new [fiber][] and executes the code in that. Your
code can in turn spawn whatever other fibers it wants. It keeps running fibers
until they all complete or the current one [suspends].
until they all complete or one [suspends].
[fiber]: ../concurrency.html
[suspends]: ../modules/core/fiber.html#fiber.suspend()
The code runs in a special "main" module. Everytime you call `wrenInterpret()`,
it runs in the same module. That way, top-level names defined in one call can be
accessed in later ones.
If a [runtime error][] occurs (and another fiber doesn't handle it), Wren abort
If a [runtime error][] occurs (and another fiber doesn't handle it), Wren aborts
fibers all the way back to the main one and returns `WREN_RESULT_RUNTIME_ERROR`.
Otherwise, when the last fiber successfully returns, it returns
`WREN_RESULT_SUCCESS`.
[runtime error]: error-handling.html
[runtime error]: ../error-handling.html
All code passed to `wrenInterpret()` runs in a special "main" module. That way,
top-level names defined in one call can be accessed in later ones. It's similar
to a REPL session.
## Shutting down a VM
Once the party is over and you're ready to end your relationship with a VM, you
need to free any memory it allocated. You do that like so:
:::c
wrenFreeVM(vm);
<pre class="snippet" data-lang="c">
wrenFreeVM(vm);
</pre>
After calling that, you obviously cannot use the `WrenVM*` you passed to it
again. It's dead.
@ -174,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 &lt;stdio.h>
#include "wren.h"
static void writeFn(WrenVM* vm, const char* text)
{
printf("%s", text);
}
void errorFn(WrenVM* vm, WrenErrorType errorType,
const char* module, const int line,
const char* msg)
{
switch (errorType)
{
case WREN_ERROR_COMPILE:
{
printf("[%s line %d] [Error] %s\n", module, line, msg);
} break;
case WREN_ERROR_STACK_TRACE:
{
printf("[%s line %d] in %s\n", module, line, msg);
} break;
case WREN_ERROR_RUNTIME:
{
printf("[Runtime Error] %s\n", msg);
} break;
}
}
int main()
{
WrenConfiguration config;
wrenInitConfiguration(&config);
config.writeFn = &writeFn;
config.errorFn = &errorFn;
WrenVM* vm = wrenNewVM(&config);
const char* module = "main";
const char* script = "System.print(\"I am running in a VM!\")";
WrenInterpretResult result = wrenInterpret(vm, module, script);
switch (result) {
case WREN_RESULT_COMPILE_ERROR:
{ printf("Compile Error!\n"); } break;
case WREN_RESULT_RUNTIME_ERROR:
{ printf("Runtime Error!\n"); } break;
case WREN_RESULT_SUCCESS:
{ printf("Success!\n"); } break;
}
wrenFreeVM(vm);
}
</pre>
[handle]: slots-and-handles.html#handles
Next, we'll learn to make that VM do useful stuff...

View File

@ -1,9 +1,8 @@
^title Slots and Handles
With `wrenInterpret()`, we can execute code, but that code can't do anything
particularly interesting. Out of the box, the VM is very isolated from the rest
of the world, so pretty much all it can do is burn CPU cycles. It can turn your
laptop into a lap warmer, but that's about it.
particularly interesting. By default, the VM is isolated from the rest of the
world, so pretty much all it can do is turn your laptop into a lap warmer.
To make our Wren code *useful*, the VM needs to communicate with the outside
world. Wren uses a single unified set of functions for passing data into and out
@ -14,28 +13,25 @@ of the VM. These functions are based on two fundamental concepts: **slots** and
When you want to send data to Wren, read data from it, or generally monkey
around with Wren objects from C, you do so by going through an array of slots.
You can think of it as a shared message board that both the VM and your C code
can leave bits of data on for the other side to process.
Think of it as a shared message board that both the VM and your C code leave
notes on for the other side to process.
The array is zero-based, and each slot holds a value of any type. It is
dynamically sized, but it's your responsibility to ensure there are enough
slots before you start using them. You do this by calling:
The array is zero-based, and each slot can hold a value of any type. It is
dynamically sized, but it's your responsibility to ensure there are enough slots
*before* you use them. You do this by calling:
:::c
wrenEnsureSlots(WrenVM* vm, int slotCount);
<pre class="snippet" data-lang="c">
wrenEnsureSlots(WrenVM* vm, int slotCount);
</pre>
This grows the slot array if needed to ensure that many slots are available. If
it's already big enough, this does nothing. You'll typically call this once
before populating the slots with data that you want to send to Wren.
:::c
wrenEnsureSlots(vm, 4);
// Can now use slots 0 through 3, inclusive.
When Wren is [calling your C code][] and passing data to you, it ensures there
are enough slots for the objects it is sending you.
[calling your c code]: calling-c-from-wren.html
<pre class="snippet" data-lang="c">
wrenEnsureSlots(vm, 4);
// Can now use slots 0 through 3, inclusive.
</pre>
After you ensure an array of slots, you can only rely on them being there until
you pass control back to Wren. That includes calling `wrenCall()` or
@ -47,24 +43,32 @@ If you read or write from a slot that you haven't ensured is valid, Wren makes
no guarantees about what will happen. I've heard rumors of smoke and feathers
flying out of a user's computer.
If you want to see how big the slot array is, you can call:
If you want to see how big the slot array is, use:
:::c
int wrenGetSlotCount(WrenVM* vm);
<pre class="snippet" data-lang="c">
int wrenGetSlotCount(WrenVM* vm);
</pre>
It returns the number of slots in the array. Note that this may be higher than
the size you've ensured. Wren reuses the memory for this array when possible,
so you may get one bigger than you need if it happened to be laying around.
When Wren [calls your C code][] and passes data to you, it ensures there are
enough slots for the objects it is sending you.
[calls your c code]: calling-c-from-wren.html
### Writing slots
Once you have some slots, you can store data in them using a number of functions all named `wrenSetSlot<type>()` where `<type>` is the kind of data.
We'll start with the simple ones:
Once you have some slots, you store data in them using a number of functions all
named `wrenSetSlot<type>()` where `<type>` is the kind of data. We'll start with
the simple ones:
:::c
void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
void wrenSetSlotNull(WrenVM* vm, int slot);
<pre class="snippet" data-lang="c">
void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
void wrenSetSlotNull(WrenVM* vm, int slot);
</pre>
Each of these takes a primitive C value and converts it to the corresponding
[Wren value][]. (Since Wren's [native number type][] *is* a double, there's not
@ -75,20 +79,21 @@ really much *conversion* going on, but you get the idea.)
You can also pass string data to Wren:
:::c
void wrenSetSlotBytes(WrenVM* vm, int slot,
const char* bytes, size_t length);
<pre class="snippet" data-lang="c">
void wrenSetSlotBytes(WrenVM* vm, int slot,
const char* bytes, size_t length);
void wrenSetSlotString(WrenVM* vm, int slot,
const char* text);
void wrenSetSlotString(WrenVM* vm, int slot,
const char* text);
</pre>
Both of these copy the bytes into a new [String][] object managed by Wren's
garbage collector, so you can free your copy of it after you call this. The
difference between the two is that `wrenSetSlotBytes()` takes an explicit
length. Since Wren supports strings containing arbitrary byte values, including
the null byte, this lets you pass those in. It's also a little faster to use
this for regular strings if you happen to know the length. The latter calculates
the length of the string using `strlen()`.
length. Since Wren strings may contain arbitrary byte values, including the null
byte, this lets you pass those in. It's also a little faster to use this for
regular strings if you happen to know the length. The latter calculates the
length of the string using `strlen()`.
[string]: ../values.html#strings
@ -96,17 +101,19 @@ the length of the string using `strlen()`.
You can, of course, also pull data out of slots. Here are the simple ones:
:::c
bool wrenGetSlotBool(WrenVM* vm, int slot);
double wrenGetSlotDouble(WrenVM* vm, int slot);
<pre class="snippet" data-lang="c">
bool wrenGetSlotBool(WrenVM* vm, int slot);
double wrenGetSlotDouble(WrenVM* vm, int slot);
</pre>
These take a Wren value of the corresponding type and convert it to its raw C
representation. For strings, we have:
:::c
const char* wrenGetSlotString(WrenVM* vm, int slot);
const char* wrenGetSlotBytes(WrenVM* vm, int slot,
int* length);
<pre class="snippet" data-lang="c">
const char* wrenGetSlotString(WrenVM* vm, int slot);
const char* wrenGetSlotBytes(WrenVM* vm, int slot,
int* length);
</pre>
These return a pointer to the first byte of the string. If you want to know the
length, the latter stores it in the variable pointed to by `length`. Both of
@ -122,11 +129,12 @@ you're gonna have a bad time.
Fortunately, you usually know what type of data you have in a slot. If not, you
can ask:
:::c
WrenType wrenGetSlotType(WrenVM* vm, int slot);
<pre class="snippet" data-lang="c">
WrenType wrenGetSlotType(WrenVM* vm, int slot);
</pre>
This returns an enum defining what type of value is in the slot. It only covers
the primitive values that are supported by the C API. Thinks like ranges and
the primitive values that are supported by the C API. Things like ranges and
instances of classes come back as `WREN_TYPE_UNKNOWN`. If you want to move that
kind of data between Wren and C, you'll have to pull the object apart into
simple primitive values first or use a [foreign class][].
@ -138,23 +146,21 @@ simple primitive values first or use a [foreign class][].
There are a few other utility functions that move data into and out of slots.
Here's the first:
:::c
void wrenGetVariable(WrenVM* vm, const char* module,
const char* name, int slot);
<pre class="snippet" data-lang="c">
void wrenGetVariable(WrenVM* vm, const char* module,
const char* name, int slot);
</pre>
This looks up a top level variable with the given name in the module with the
given name and stores its value in the given slot. (Code you executed using
`wrenInterpret()` implicitly goes into a module named "main".)
given name and stores its value in the given slot. Note that classes are just
objects stored in variables too, so you can use this to look up a class by its
name. Handy for calling static methods on it.
Note that classes are just objects stored in variables too, so you can use this
to look up a class by its name. Handy for calling static methods on it.
Like any method that works with strings, this one is a bit slow. It has to
hash the name and look it up in the module's string table. You might want to
avoid calling this in the middle of a hot loop where performance is critical.
Instead, it's faster to look up the variable once outside the loop and store a
reference to the object using a [WrenHandle](#handles).
Like any method that works with strings, this one is a bit slow. It has to hash
the name and look it up in the module's string table. You might want to avoid
calling this in the middle of a hot loop where performance is critical. Instead,
it's faster to look up the variable once outside the loop and store a reference
to the object using a [handle](#handles).
### Working with lists
@ -167,16 +173,18 @@ with them directly.
You can create a new empty list from C using:
:::c
void wrenSetSlotNewList(WrenVM* vm, int slot);
<pre class="snippet" data-lang="c">
void wrenSetSlotNewList(WrenVM* vm, int slot);
</pre>
It stores the resulting list in the given slot. If you have a list in a
slot&mdash;either one you created from C or from Wren&mdash;you can add elements
to it using:
:::c
void wrenInsertInList(WrenVM* vm, int listSlot, int index,
int elementSlot);
<pre class="snippet" data-lang="c">
void wrenInsertInList(WrenVM* vm, int listSlot, int index,
int elementSlot);
</pre>
That's a lot of int parameters:
@ -211,44 +219,41 @@ have two limitations:
that aren't simple primitive ones. If you want to grab a reference to,
say, an instance of some class, how do you do it?
To address those, we have WrenHandle. A WrenHandle wraps a reference to an
object of any kind&mdash;strings, numbers, instances of classes, collections,
whatever.
To address those, we have handles. A handle wraps a reference to an object of
any kind&mdash;strings, numbers, instances of classes, collections, whatever.
You create a handle using this:
You create a WrenHandle using this:
:::c
WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
<pre class="snippet" data-lang="c">
WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
</pre>
This takes the object stored in the given slot, creates a new WrenHandle to wrap
it, and returns a pointer to it back to you.
it, and returns a pointer to it back to you. You can send that wrapped object
back to Wren by calling:
You can send that wrapped object back to Wren by calling:
:::c
void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
<pre class="snippet" data-lang="c">
void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
</pre>
Note that this doesn't invalidate your WrenHandle. You can still keep using it.
### Retaining and releasing handles
A WrenHandle is an opaque wrapper around an object of any type, but just as
A handle is an opaque wrapper around an object of any type, but just as
important, it's a *persistent* one. When Wren gives you a pointer to a
WrenHandle, it guarantees that that pointer remains valid. You can keep it
around as long as you want. Even if a garbage collection occurs, Wren will
ensure all of the WrenHandles and the objects they wrap are kept safely in
memory.
ensure the handle and the object it wraps are kept safely in memory.
Internally, Wren keeps a list of all of the WrenHandles that have been created.
That way, during garbage collection, it can find them all and make sure their
objects aren't freed.
objects aren't freed. But what if you don't want it to be kept around any more?
Since C relies on manual memory management, WrenHandle does too. When you are
done with one, you must explicitly release it by calling:
But what if you don't want it to be kept around any more? Since C relies on
manual memory management, WrenHandle does too. When you are done with one, you
must explicitly release it by calling:
:::c
void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
<pre class="snippet" data-lang="c">
void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
</pre>
This does not immediately delete the wrapped object&mdash;after all, there may
be other references to the same object in the program. It just invalidates the

View File

@ -1,10 +1,389 @@
^title Storing C Data
**TODO: Write these docs.**
An embedded language often needs to work with native data. You may want a
pointer to some memory managed in the C heap, or maybe you want to store a chunk
of data more efficiently than Wren's dynamism allows. You may want a Wren object
that represents a native resource like a file handle or database connection.
Until these are written, you can read the docs in [wren.h][].
For those cases, you can define a **foreign class**, a chimera whose state is
half Wren and half C. It is a real Wren class with a name, constructor, and
methods. You can define methods on it written in Wren, or [foreign methods][]
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.
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
[foreign methods]: calling-c-from-wren.html
## Defining a Foreign Class
You define one like so:
<pre class="snippet">
foreign class Point {
// ...
}
</pre>
The `foreign` keyword tells Wren to loop in the host application when it
constructs instances of the class. The host tells Wren how many bytes of extra
memory the foreign instance should contain and in return, Wren gives the host
the opportunity to initialize that data.
To talk to the host app, Wren needs a C function it can call when it constructs
an instance of the foreign class. This function is found through a binding
process similar to [how foreign methods are bound][bind]. When you [configure
the VM][], you set the `bindForeignClassFn` field in WrenConfiguration to point
to a C callback you define. Its signature must be:
[bind]: calling-c-from-wren.html#binding-foreign-methods
[configure the vm]: configuring-the-vm.html
<pre class="snippet" data-lang="c">
WrenForeignClassMethods bindForeignClass(
WrenVM* vm, const char* module, const char* className);
</pre>
Wren invokes this callback once when a foreign class declaration is executed.
Wren passes in the name of the module containing the foreign class, and the name
of the class being declared. The host's responsibility is to return one of these
structs:
<pre class="snippet" data-lang="c">
typedef struct
{
WrenForeignMethodFn allocate;
WrenFinalizerFn finalize;
} WrenForeignClassMethods;
</pre>
It's a pair of function pointers. The first, `allocate`, is called by Wren
whenever an instance of the foreign class is created. (We'll get to the optional
`finalize` callback later.) The allocation callback has the same signature as a
foreign method:
<pre class="snippet" data-lang="c">
void allocate(WrenVM* vm);
</pre>
## Initializing an Instance
When you create an instance of a foreign class by calling one its
[constructors][], Wren invokes the `allocate` callback you gave it when binding
the foreign class. Your primary responsibility in that callback is to tell Wren
how many bytes of raw memory you need. You do that by calling:
[constructors]: ../classes.html#constructors
<pre class="snippet" data-lang="c">
void* wrenSetSlotNewForeign(WrenVM* vm,
int slot, int classSlot, size_t size);
</pre>
Like other [slot manipulation functions][slot], it both reads from and writes to
the slot array. It has a few parameters to make it more general purpose since it
can also be used in other foreign methods:
[slot]: slots-and-handles.html
* The `slot` parameter is the destination slot where the new foreign object
should be placed. When you're calling this in a foreign class's allocate
callback, this should be 0.
* The `classSlot` parameter is the slot where the foreign class being
constructed can be found. When the VM calls an allocate callback for a
foreign class, the class itself is already in slot 0, so you'll pass 0 for
this too.
* Finally, the `size` parameter is the interesting one. Here, you pass in the
number of extra raw bytes of data you want the foreign instance to store.
This is the memory you get to play with from C.
So, for example, if you wanted to create a foreign instance that contains eight
bytes of C data, you'd call:
<pre class="snippet" data-lang="c">
void* data = wrenSetSlotNewForeign(vm, 0, 0, 8);
</pre>
The value returned by `wrenSetSlotNewForeign()` is the raw pointer to the
requested bytes. You can cast that to whatever C type makes sense (as long as it
fits within the requested number of bytes) and initialize it as you see fit.
Any parameters passed to the constructor are also available in subsequent slots
in the slot array. That way you can initialize the foreign data based on values
passed to the constructor from Wren.
After the allocate callback returns, the class's constructor in Wren is run and
execution proceeds like normal. From here on out, within Wren, it appears you
have a normal instance of a class. It just happens to have some extra bytes
hiding inside it that can be accessed from foreign methods.
## Accessing Foreign Data
Typically, the way you make use of the data stored in an instance of a foreign
class is through other foreign methods. Those are usually defined on the same
foreign class, but can be defined on other classes as well. Wren doesn't care.
Once you have a foreign instance in a slot, you can access the raw bytes it
stores by calling:
<pre class="snippet" data-lang="c">
void* wrenGetSlotForeign(WrenVM* vm, int slot);
</pre>
You pass in the slot index containing the foreign object and it gives you back a
pointer to the raw memory the object wraps. As usual, the C API doesn't do any
type or bounds checking, so it's on you to make sure the object in that slot
actually *is* an instance of a foreign class and contains as much memory as you
access.
Given that void pointer, you can now freely read and modify the data it points
to. They're your bits, Wren just holds them for you.
## Freeing Resources
If your foreign instances are just holding memory and you're OK with Wren's
garbage collector managing the lifetime of that memory, then you're done. Wren
will keep the bytes around as long as there is still a reference to them. When
the instance is no longer reachable, eventually the garbage collector will do
its thing and free the memory.
But, often, your foreign data refers to some resource whose lifetime needs to
be explicitly managed. For example, if you have a foreign object that wraps an
open file handle, you need to ensure that handle doesn't get left open when the
GC frees the foreign instance.
Of course, you can (and usually should) add a method on your foreign class, like
`close()` so the user can explicitly release the resource managed by the object.
But if they forget to do that and the object is no longer reachable, you want to
make sure the resource isn't leaked.
To that end, you can also provide a *finalizer* function when binding the
foreign class. That's the other callback in the WrenForeignClassMethods struct.
If you provide that callback, then Wren will invoke it when an instance of your
foreign class is about to be freed by the garbage collector. This gives you one
last chance to clean up the object's resources.
Because this is called during the middle of a garbage collection, you do not
have unfettered access to the VM. It's not like a normal foreign method where
you can monkey around with slots and other stuff. Doing that while the GC is
running could leave Wren in a weird state.
Instead, the finalize callback's signature is only:
<pre class="snippet" data-lang="c">
void finalize(void* data);
</pre>
Wren gives you the pointer to your foreign function's memory, and that's it. The
*only* thing you should do inside a finalizer is release any external resources
referenced by that memory.
## A Full Example
That's a lot to take in, so let's walk through a full example of a foreign class
with a finalizer and a couple of methods. We'll do a File class that wraps the
C standard file API.
In Wren, the class we want looks like this:
<pre class="snippet">
foreign class File {
construct create(path) {}
foreign write(text)
foreign close()
}
</pre>
So you can create a new file given a path. Once you have one, you can write to
it and then explicitly close it if you want. We also need to make sure the file
gets closed if the user forgets to and the GC cleans up the object.
### Setting up the VM
Over in the host, first we'll set up the VM:
<pre class="snippet" data-lang="c">
#include "wren.h"
int main(int argc, const char* argv[])
{
WrenConfiguration config;
wrenInitConfiguration(&config);
config.bindForeignClassFn = bindForeignClass;
config.bindForeignMethodFn = bindForeignMethod;
WrenVM* vm = wrenNewVM(&config);
wrenInterpret(vm, "my_module", "some code...");
return 0;
}
</pre>
### Binding the foreign class
We give the VM two callbacks. The first is for wiring up the foreign class
itself:
<pre class="snippet" data-lang="c">
WrenForeignClassMethods bindForeignClass(
WrenVM* vm, const char* module, const char* className)
{
WrenForeignClassMethods methods;
if (strcmp(className, "File") == 0)
{
methods.allocate = fileAllocate;
methods.finalize = fileFinalize;
}
else
{
// Unknown class.
methods.allocate = NULL;
methods.finalize = NULL;
}
return methods;
}
</pre>
When our binding callback is invoked for the File class, we return the allocate
and finalize functions the VM should call. Allocation looks like:
<pre class="snippet" data-lang="c">
#include &lt;stdio.h>
#include "wren.h"
void fileAllocate(WrenVM* vm)
{
FILE** file = (FILE**)wrenSetSlotNewForeign(vm,
0, 0, sizeof(FILE*));
const char* path = wrenGetSlotString(vm, 1);
*file = fopen(path, "w");
}
</pre>
First we create the instance by calling `wrenSetSlotNewForeign()`. We tell it to
add enough extra bytes to store a `FILE*` in it, which is C's representation of
a file handle. We're given back a pointer to the bytes. Since the file handle is
itself a pointer, we end up with a double indirection, hence the `FILE**`. In
most cases, you'll just have a single `*`.
We also pull the file path from the slot array. Then we tell C to create a new
file at that path. That gives us back a new file handle -- a `FILE*` -- and we
store that back into the foreign instance using `*file`. Now we have a foreign
object that wraps an open file handle.
The finalizer simply casts the foreign instance's data back to the proper type
and closes the file:
<pre class="snippet" data-lang="c">
void fileFinalize(void* data)
{
closeFile((FILE**) data);
}
</pre>
It uses this little utility function:
<pre class="snippet" data-lang="c">
static void closeFile(FILE** file)
{
// Already closed.
if (*file == NULL) return;
fclose(*file);
*file = NULL;
}
</pre>
This closes the file (if it's not already closed) and also nulls out the file
handle so that we don't try to use the file after it's been closed.
### Binding the foreign methods
That's the foreign *class* part. Now we have a couple of foreign *methods* to
handle. The host tells the VM how to find them by giving Wren a pointer to this
function:
<pre class="snippet" data-lang="c">
WrenForeignMethodFn bindForeignMethod(WrenVM* vm, const char* module,
const char* className, bool isStatic, const char* signature)
{
if (strcmp(className, "File") == 0)
{
if (!isStatic && strcmp(signature, "write(_)") == 0)
{
return fileWrite;
}
if (!isStatic && strcmp(signature, "close()") == 0)
{
return fileClose;
}
}
// Unknown method.
return NULL;
}
</pre>
When Wren calls this, we look at the class and method name to figure out which
method it's binding, and then return a pointer to the appropriate function. The
foreign method for writing to the file is:
<pre class="snippet" data-lang="c">
void fileWrite(WrenVM* vm)
{
FILE** file = (FILE**)wrenGetSlotForeign(vm, 0);
// Make sure the file is still open.
if (*file == NULL)
{
wrenSetSlotString(vm, 0, "Cannot write to a closed file.");
wrenAbortFiber(vm, 0);
return;
}
const char* text = wrenGetSlotString(vm, 1);
fwrite(text, sizeof(char), strlen(text), *file);
}
</pre>
We use `wrenGetSlotForeign()` to pull the foreign data out of the slot array.
Since this method is called on the file itself, the foreign object is in slot
zero. We take the resulting pointer and cast it to a pointer of the proper type.
Again, because our foreign data is *itself* a pointer, we get a pointer to a
pointer.
We do a little sanity checking to make sure the user isn't writing to a file
they already closed. If not, we call `fwrite()` to write to the file.
The other method is `close()` to let them explicitly close the file:
<pre class="snippet" data-lang="c">
void fileClose(WrenVM* vm)
{
FILE** file = (FILE**)wrenGetSlotForeign(vm, 0);
closeFile(file);
}
</pre>
It uses the same helper we defined above. And that's it, a complete foreign
class with a finalizer and a couple of foreign methods. In Wren, you can use it
like so:
<pre class="snippet">
var file = File.create("some/path.txt")
file.write("some text")
file.close()
</pre>
Pretty neat, right? The resulting class looks and feels like a normal Wren
class, but it has the functionality and much of the performance of native C
code.
<a class="right" href="configuring-the-vm.html">Configuring the VM &rarr;</a>
<a href="calling-c-from-wren.html">&larr; Calling C from Wren</a>

View File

@ -3,6 +3,9 @@
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<script type="application/javascript" src="../prism.js" data-manual></script>
<script type="application/javascript" src="../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../prism.css" />
<link rel="stylesheet" type="text/css" href="../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
@ -20,6 +23,10 @@
</header>
<div class="page">
<nav class="big">
<a href="../"><img src="../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Back to Wren</a></li>
</ul>
<section>
<h2>embedding</h2>
<ul>
@ -29,7 +36,6 @@
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
<li><a href="storing-c-data.html">Storing C Data</a></li>
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
<li><a href="application-lifecycle.html">Application Lifecycle</a></li>
</ul>
</section>
</nav>
@ -49,7 +55,6 @@
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
<li><a href="storing-c-data.html">Storing C Data</a></li>
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
<li><a href="application-lifecycle.html">Application Lifecycle</a></li>
</ul>
</td>
<td>
@ -72,10 +77,10 @@
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/munificent/wren">on GitHub</a>
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>

View File

@ -7,26 +7,26 @@ Errors come in a few fun flavors.
The first errors you're likely to run into are syntax errors. These include
simple bugs where your code doesn't follow the language's grammar, like:
:::wren
1 + * 2
<pre class="snippet">
1 + * 2
</pre>
Wren detects these errors as soon as it tries to read your code. When it hits
one, you get a friendly error message, like:
:::text
[main line 1] Error on '*': Unexpected token for expression.
Some slightly more "semantic" errors fall into this bucket too. Things like
using a variable that hasn't been defined, or declaring two variables with the
same name in the same scope. So if you do:
:::wren
var a = "once"
var a = "twice"
<pre class="snippet">
var a = "once"
var a = "twice"
</pre>
Wren tells you:
:::text
[main line 2] Error on 'a': Top-level variable is already defined.
Note that it does this before it executes *any* code. Unlike some other
@ -48,17 +48,17 @@ perform an operation that the VM can't do. The most common error is a "method
not found" one. If you call a method on an object and its class (and all of its
superclasses) don't define that method, there's nothing Wren can do:
:::wren
class Foo {
construct new() {}
}
<pre class="snippet">
class Foo {
construct new() {}
}
var foo = Foo.new()
foo.someRandomMethod
var foo = Foo.new()
foo.someRandomMethod
</pre>
If you run this, Wren will print:
:::text
Foo does not implement method 'someRandomMethod'.
Then it stops executing code. Unlike some other languages, Wren doesn't keep
@ -71,13 +71,13 @@ Another common runtime error is passing an argument of the wrong type to a
method. For example, lists are indexed using a number. If you try to pass some
other type, it's an error:
:::wren
var list = ["a", "b", "c"]
list["1"]
<pre class="snippet">
var list = ["a", "b", "c"]
list["1"]
</pre>
This exits with:
:::text
Subscript must be a number or a range.
[main line 2] in (script)
@ -104,29 +104,29 @@ error message as a string.
For example, if you run this program:
:::wren
var fiber = Fiber.new {
123.badMethod
}
<pre class="snippet">
var fiber = Fiber.new {
123.badMethod
}
var error = fiber.try()
System.print("Caught error: " + error)
var error = fiber.try()
System.print("Caught error: " + error)
</pre>
It prints:
:::text
Caught error: Num does not implement method 'badMethod'.
The called fiber can no longer be used, but any other fibers can proceed as
usual. When a fiber has been aborted because of a runtime error, you can also
get the error from the fiber object. Continuing the above example:
:::wren
System.print(fiber.error)
<pre class="snippet">
System.print(fiber.error)
</pre>
This also prints:
:::text
Num does not implement method 'badMethod'.
If you have a chain of fiber calls and a runtime error occurs, it will walk the
@ -139,11 +139,14 @@ Most runtime errors come from within the Wren VM, but you may want to be able
to cause your own runtime errors to occur. This can be done by calling the
`abort()` static method on `Fiber`:
:::wren
Fiber.abort("Something bad happened")
<pre class="snippet">
Fiber.abort("Something bad happened")
</pre>
You must pass in an error message, and it must be a string.
If the provided message is `null`, no runtime error is raised.
## Failures
The last flavor of errors is the highest-level one. All of the above errors
@ -166,5 +169,6 @@ For example, a method for parsing a number could return a number on success and
`null` to indicate parsing failed. Since Wren is dynamically typed, it's easy
and natural for a method to return different types of values.
<br><hr>
<a class="right" href="modularity.html">Modularity &rarr;</a>
<a href="concurrency.html">&larr; Concurrency</a>

View File

@ -1,119 +1,44 @@
^title Functions
No self-respecting language today can get by without functions&mdash;first
class little bundles of code. Since Wren is object-oriented, most of your code
will live in methods on classes, but free-floating functions are still
eminently handy.
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.
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.
Since that's the most common usage pattern, Wren's syntax optimizes for that.
Taking a page from Ruby, a function is created by passing a *block argument* to
a method. At its simplest, it looks like this:
<pre class="snippet">
var sayHello = Fn.new { System.print("hello") }
:::wren
blondie.callMe {
System.print("This is the body!")
}
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)&mdash;everything between that pair of
curly braces.
Methods that take a block argument receive it as a normal parameter. `callMe`
could be defined like so:
:::wren
class Blondie {
callMe(fn) {
// Call it...
}
}
A method can take other arguments in addition to the block. They appear before
the block just like a regular argument list. For example:
:::wren
blondie.callMeAt(867, 5309) {
System.print("This is the body!")
}
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:
:::wren
var someFn = // Get a function...
blondie.callMe(someFn)
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:
:::wren
var someFn = Fn.new {
System.print("Hi!")
}
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:
:::wren
class Blondie {
callMe(fn) {
fn.call()
}
}
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.
:::wren
class FakeFn {
call() {
System.print("I'm feeling functional!")
}
}
blondie.callMe(FakeFn.new())
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.
:::wren
blondie.callMe {|first, last|
System.print("Hi, " + first + " " + last + "!")
}
To pass arguments to the function, pass them to the `call` method:
Here we're passing a function to `greet` that takes two parameters, `first` and
`last`. They are passed to the function when it's called:
<pre class="snippet">
var sayMessage = Fn.new {|recipient, message|
System.print("message for %(recipient): %(message)")
}
:::wren
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
expects. If you pass too *many* arguments, the extras are ignored.
@ -129,12 +54,21 @@ Otherwise, the body returns `null` by default. You can explicitly return a
value using a `return` statement. In other words, these two functions do the
same thing:
:::wren
Fn.new { "return value" }
<pre class="snippet">
Fn.new { "return value" }
Fn.new {
return "return value"
}
Fn.new {
return "return value"
}
</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
@ -142,24 +76,167 @@ As you expect, functions are closures&mdash;they can access variables defined
outside of their scope. They will hold onto closed-over variables even after
leaving the scope where the function is defined:
:::wren
class Counter {
static create() {
var i = 0
return Fn.new { i = i + 1 }
}
}
<pre class="snippet">
class Counter {
static create() {
var i = 0
return Fn.new { i = i + 1 }
}
}
</pre>
Here, the `create` method returns the function created on its second line. That
function references a variable `i` declared outside of the function. Even after
the function is returned from `create`, it is still able to read and assign
to`i`:
:::wren
var counter = Counter.create()
System.print(counter.call()) //> 1
System.print(counter.call()) //> 2
System.print(counter.call()) //> 3
<pre class="snippet">
var counter = Counter.create()
System.print(counter.call()) //> 1
System.print(counter.call()) //> 2
System.print(counter.call()) //> 3
</pre>
## Callable classes
Because `Fn` is a class, and responds to `call()`, any class can respond to
`call()` and be used in place of a function. This is particularly handy when
the function is passed to a method to be called, like a callback or event.
<pre class="snippet">
class Callable {
construct new() {}
call(name, version) {
System.print("called %(name) with version %(version)")
}
}
var fn = Callable.new()
fn.call("wren", "0.4.0")
</pre>
## Block arguments
Very frequently, functions are passed to methods to be called. There are
countless examples of this in Wren, like [list](lists.html) can be filtered
using a method `where` which accepts a function:
<pre class="snippet">
var list = [1, 2, 3, 4, 5]
var filtered = list.where(Fn.new {|value| value > 3 })
System.print(filtered.toList) //> [4, 5]
</pre>
This syntax is a bit less fun to read and write, so Wren implements the
_block argument_ concept. When a function is being passed to a method,
and is the last argument to the method, it can use a shorter syntax:
_just the block part_.
Let's use a block argument for `list.where`, it's the last (only) argument:
<pre class="snippet">
var list = [1, 2, 3, 4, 5]
var filtered = list.where {|value| value > 3 }
System.print(filtered.toList) //> [4, 5]
</pre>
We've seen this before in a previous page using `map` and `where`:
<pre class="snippet">
numbers.map {|n| n * 2 }.where {|n| n < 100 }
</pre>
## Block argument example
Let's look at a complete example, so we can see both ends.
Here's a fictional class for something that will call a function
when a click event is sent to it. It allows us to pass just a
function and assume the left mouse button, or to pass a button and a function.
<pre class="snippet">
class Clickable {
construct new() {
_fn = null
_button = 0
}
onClick(fn) {
_fn = fn
}
onClick(button, fn) {
_button = button
_fn = fn
}
fireEvent(button) {
if(_fn && button == _button) {
_fn.call(button)
}
}
}
</pre>
Now that we've got the clickable class, let's use it.
We'll start by using the method that accepts just a function
because we're fine with it just being the default left mouse button.
<pre class="snippet">
var link = Clickable.new()
link.onClick {|button|
System.print("I was clicked by button %(button)")
}
// send a left mouse click
// normally this would happen from elsewhere
link.fireEvent(0) //> I was clicked by button 0
</pre>
Now let's try with the extra button argument:
<pre class="snippet">
var contextMenu = Clickable.new()
contextMenu.onClick(1) {|button|
System.print("I was right-clicked")
}
link.fireEvent(0) //> (nothing happened)
link.fireEvent(1) //> I was right-clicked
</pre>
Notice that we still pass the other arguments normally,
it's only the last argument that is special.
**Just a regular function**
Block arguments are purely syntax sugar for creating a function and passing it
in one little blob of syntax. These two are equivalent:
<pre class="snippet">
onClick(Fn.new { System.print("clicked") })
onClick { System.print("clicked") }
</pre>
And this is just as valid:
<pre class="snippet">
var onEvent = Fn.new {|button|
System.print("clicked by button %(button)")
}
onClick(onEvent)
onClick(1, onEvent)
</pre>
**Fn.new**
As you may have noticed by now, `Fn` accepts a block argument for the `Fn.new`.
All the constructor does is return that argument right back to you!
<br><hr>
<a class="right" href="classes.html">Classes &rarr;</a>
<a href="variables.html">&larr; Variables</a>

View File

@ -1,109 +1,89 @@
^title Getting Started
Getting Wren running on your machine is straightforward. Tiny C programs with
few dependencies are nice that way. "Wren" encompasses two separate artifacts:
## Trying out the language
* **The virtual machine.** This is the core chunk of C that executes Wren
If you'd like to try Wren, you have a few options.
* **In your browser.** You can try Wren **[right here](./try/)**!
* **On your computer.** The [Wren CLI](cli) project is a downloadable executable
to run scripts with access to file io and more. See the [Wren CLI docs](cli).
* **Embedded in your code.** See how to [build and embed Wren](#embed-the-vm) below.
And then read the [embedding guide](embedding)!
Once you have somewhere to explore, it's time to [learn the
language](syntax.html).
---
## Embed the VM
**The Wren Virtual Machine** is the core of the language that executes Wren
source code. It is just a library, not a standalone application. It's
designed to be [embedded][] in a larger host application. It has no
dependencies beyond the C standard library. You can use it as a static
library, shared library, or simply compile the source into your app.
designed to be [embedded][] in a larger host application.
* **The command line executable.** Wren also ships with a CLI wrapper around
the VM. This gives you a way to run Wren code from the command-line, and
also includes modules for talking to the operating system&mdash;file IO,
networking, stuff like that. It depends on [libuv][] for that
functionality.
It has no dependencies beyond the C standard library.
You can use it as a static library, shared library, or simply compile the source into your app.
[embedded]: embedding-api.html
[libuv]: http://libuv.org/
### Building Wren
If you're on a Unix or Mac and you can rock a command line, it's just:
To build the Wren library, we look inside the `projects/` folder.
In here you'll find ready to go projects for `Visual Studio`, `XCode` and tools like `make`.
:::sh
$ git clone https://github.com/munificent/wren.git
$ cd wren
$ make
$ ./wren
* **Windows** Open `wren.sln` inside `projects/vs2019/` (or `vs2017`), hit build.
* **Mac** Open `wren.xcworkspace` inside `projects/xcode/`, hit build.
* **Linux** Run `make` inside of `projects/make/`.
This builds both the VM and the CLI. It downloads libuv automatically for you.
The release build of the CLI goes right into the repo's top level directory.
Binaries for other configurations are built to `bin/`. Static and shared
libraries for embedding Wren get built in `lib/`.
In each case, **there will be library files generated into the root `lib/` folder**.
These are what you'll link into your project, based on your needs.
For Mac users, there is also an XCode project under `util/xcode`. For
Windows brethren, `util/msvc2013` contains a Visual Studio solution. Note
that these may not have the exact same build settings as the makefile. The
makefile is the "official" way to compile Wren.
* **Static Linking** `wren.lib` on Windows, `libwren.a` elsewhere.
* **Dynamic Linking** `wren.dll` on Windows, `libwren.so` on Linux, and `libwren.dylib` on Mac.
If you only want to build the VM, you can do:
<small>
Note that the default build will also generate `wren_test` inside of `bin/`,
a binary that is used to run the language tests. It can execute simple scripts.
</small>
:::sh
$ make vm
**Other platforms**
If your platform isn't explicitly supported,
it is recommended that you include the Wren source
in your project for a portable experience.
This compiles the VM to static and shared libraries. It does not even download
libuv since it isn't needed.
### Including the code in your project
## Interactive mode
**all source files**
The alternative to building via the provided projects is to include the wren source code in your project.
Since it has no dependencies this is simple, all the code in `src/` comes along. There's a readme in `src/` for details.
If you just run `wren` without any arguments, it starts the interpreter in
interactive mode. You can type in a line of code, and it immediately executes
it. Here's something to try:
**'amalgamated' build**
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_.
:::wren
System.print("Hello, world!")
This file can be generated by running `python3 util/generate_amalgamation.py > build/wren.c`,
which saves the generated output in `build/wren.c`.
Or a little more exciting:
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>
:::wren
for (i in 1..10) System.print("Counting up %(i)")
---
You can exit the interpreter using good old Ctrl-C or Ctrl-D, or just throw
your computer to the ground and storm off.
[embedded]: embedding
## 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
your favorite text editor and paste this into it:
:::wren
for (yPixel in 0...24) {
var y = yPixel / 12 - 1
for (xPixel in 0...80) {
var x = xPixel / 30 - 2
var x0 = x
var y0 = y
var iter = 0
while (iter < 11 && x0 * x0 + y0 * y0 <= 4) {
var x1 = (x0 * x0) - (y0 * y0) + x
var y1 = 2 * x0 * y0 + y
x0 = x1
y0 = y1
iter = iter + 1
}
System.write(" .-:;+=xX$& "[iter])
}
System.print("")
}
Now run:
:::sh
$ ./wren my_script.wren
Neat, right? You're a Wren programmer now! The next step is to [learn the
language](syntax.html). If you run into bugs, or have ideas or questions, any of
If you run into bugs, or have ideas or questions, any of
the following work:
* **Ask on the [Wren mailing list][list].**
* Tell me on twitter at [@munificentbob][twitter].
* Join the [discord community][discord].
* Ask on the [Wren mailing list][list] (which is pretty quiet).
* Tell us on twitter at [@munificentbob][twitter] or [@ruby0x1][twitter0x1].
* [File a ticket][issue] at [the GitHub repo][repo].
* Send a pull request.
* Email me at [`robert@stuffwithstuff.com`](mailto:robert@stuffwithstuff.com).
* The CLI also has [tickets][issue_cli] and a [GitHub repo][repo_cli] too.
* Pull requests are welcome.
[discord]: https://discord.gg/Kx6PxSX
[list]: https://groups.google.com/forum/#!forum/wren-lang
[twitter]: https://twitter.com/intent/user?screen_name=munificentbob
[issue]: https://github.com/munificent/wren/issues
[repo]: https://github.com/munificent/wren
[twitter0x1]: https://twitter.com/intent/user?screen_name=ruby0x1
[issue]: https://github.com/wren-lang/wren/issues
[repo]: https://github.com/wren-lang/wren
[issue_cli]: https://github.com/wren-lang/wren-cli/issues
[repo_cli]: https://github.com/wren-lang/wren-cli

View File

@ -1,24 +1,27 @@
^title Welcome
^title
## Wren is a small, fast, class-based concurrent scripting language
---
Think Smalltalk in a Lua-sized package with a dash of Erlang and wrapped up in
a familiar, modern [syntax][].
:::wren
System.print("Hello, world!")
<pre class="snippet">
System.print("Hello, world!")
class Wren {
flyTo(city) {
System.print("Flying to %(city)")
}
}
class Wren {
flyTo(city) {
System.print("Flying to %(city)")
}
}
var adjectives = Fiber.new {
["small", "clean", "fast"].each {|word| Fiber.yield(word) }
}
var adjectives = Fiber.new {
["small", "clean", "fast"].each {|word| Fiber.yield(word) }
}
while (!adjectives.isDone) System.print(adjectives.call())
while (!adjectives.isDone) System.print(adjectives.call())
</pre>
* **Wren is small.** The VM implementation is under [4,000 semicolons][src].
You can skim the whole thing in an afternoon. It's *small*, but not
@ -33,7 +36,7 @@ a familiar, modern [syntax][].
[classes][] front and center.
* **Wren is concurrent.** Lightweight [fibers][] are core to the execution
model and let you organize your program into an army of communicating
model and let you organize your program into a flock of communicating
coroutines.
* **Wren is a scripting language.** Wren is intended for embedding in
@ -41,17 +44,19 @@ a familiar, modern [syntax][].
and [an easy-to-use C API][embedding]. It compiles cleanly as C99, C++98
or anything later.
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]!
---
You can try it [in your browser][browser]!
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/munificent/wren/tree/master/src
[nan]: https://github.com/munificent/wren/blob/46c1ba92492e9257aba6418403161072d640cb29/src/wren_value.h#L378-L433
[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
[fibers]: concurrency.html
[embedding]: embedding
[started]: getting-started.html
[browser]: http://ppvk.github.io/wren-nest/
[browser]: try
[contribute]: contributing.html

View File

@ -4,8 +4,9 @@ A list is a compound object that holds a collection of elements identified by
integer index. You can create a list by placing a sequence of comma-separated
expressions inside square brackets:
:::wren
[1, "banana", true]
<pre class="snippet">
[1, "banana", true]
</pre>
Here, we've created a list of three elements. Notice that the elements don't
have to be the same type.
@ -18,30 +19,34 @@ element you want. Like most languages, indexes start at zero:
[subscript operator]: method-calls.html#subscripts
:::wren
var hirsute = ["sideburns", "porkchops", "'stache", "goatee"]
System.print(hirsute[0]) //> sideburns
System.print(hirsute[1]) //> porkchops
<pre class="snippet">
var trees = ["cedar", "birch", "oak", "willow"]
System.print(trees[0]) //> cedar
System.print(trees[1]) //> birch
</pre>
Negative indices counts backwards from the end:
:::wren
System.print(hirsute[-1]) //> goatee
System.print(hirsute[-2]) //> 'stache
<pre class="snippet">
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:
:::wren
System.print(hirsute.count) //> 4
<pre class="snippet">
System.print(trees.count) //> 4
</pre>
## Slices and ranges
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:
:::wren
System.print(hirsute[1..2]) //> [porkchops, 'stache]
<pre class="snippet">
System.print(trees[1..2]) //> [birch, oak]
</pre>
This returns a new list containing the elements of the original list whose
indices are within the given range. Both inclusive and exclusive ranges work
@ -50,29 +55,33 @@ and do what you expect.
Negative bounds also work like they do when passing a single number, so to copy
a list, you can just do:
:::wren
hirsute[0..-1]
<pre class="snippet">
trees[0..-1]
</pre>
## Adding elements
Lists are *mutable*, meaning their contents can be changed. You can swap out an
existing element in the list using the subscript setter:
:::wren
hirsute[1] = "muttonchops"
System.print(hirsute[1]) //> muttonchops
<pre class="snippet">
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:
:::wren
hirsute.add("goatee")
System.print(hirsute.count) //> 4
<pre class="snippet">
trees.add("maple")
System.print(trees.count) //> 5
</pre>
You can insert a new element at a specific position using `insert`:
:::wren
hirsute.insert(2, "soul patch")
<pre class="snippet">
trees.insert(2, "hickory")
</pre>
The first argument is the index to insert at, and the second is the value to
insert. All elements following the inserted one will be pushed down to
@ -82,34 +91,62 @@ It's valid to "insert" after the last element in the list, but only *right*
after it. Like other methods, you can use a negative index to count from the
back. Doing so counts back from the size of the list *after* it's grown by one:
:::wren
var letters = ["a", "b", "c"]
letters.insert(3, "d") // OK: inserts at end.
System.print(letters) //> [a, b, c, d]
letters.insert(-2, "e") // Counts back from size after insert.
System.print(letters) //> [a, b, c, e, d]
<pre class="snippet">
var letters = ["a", "b", "c"]
letters.insert(3, "d") // OK: inserts at end.
System.print(letters) //> [a, b, c, d]
letters.insert(-2, "e") // Counts back from size after insert.
System.print(letters) //> [a, b, c, e, d]
</pre>
## Adding lists together
Lists have the ability to be added together via the `+` operator. This is often known as concatenation.
<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>
## 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.
:::wren
var letters = ["a", "b", "c", "d"]
letters.removeAt(1)
System.print(letters) //> [a, c, d]
To remove a specific _value_ instead, use `remove`. The first value that
matches using regular equality will be removed.
The `removeAt` method returns the removed item:
In both cases, all following items are shifted up to fill in the gap.
:::wren
System.print(letters.removeAt(1)) //> c
<pre class="snippet">
var letters = ["a", "b", "c", "d"]
letters.removeAt(1)
System.print(letters) //> [a, c, d]
letters.remove("a")
System.print(letters) //> [c, d]
</pre>
Both the `remove` and `removeAt` method return the removed item:
<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:
:::wren
hirsute.clear()
System.print(hirsute) //> []
<pre class="snippet">
trees.clear()
System.print(trees) //> []
</pre>
<br><hr>
<a class="right" href="maps.html">Maps &rarr;</a>
<a href="values.html">&larr; Values</a>

View File

@ -7,23 +7,28 @@ other languages: hash table, dictionary, association, table, etc.
You can create a map by placing a series of comma-separated entries inside
curly braces. Each entry is a key and a value separated by a colon:
:::wren
{
"George": "Harrison",
"John": "Lennon",
"Paul": "McCartney",
"Ringo": "Starr"
}
<pre class="snippet">
{
"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
@ -40,11 +45,12 @@ You add new key-value pairs to the map using the [subscript operator][]:
[subscript operator]: method-calls.html#subscripts
:::wren
var capitals = {}
<pre class="snippet">
var capitals = {}
capitals["Georgia"] = "Atlanta"
capitals["Idaho"] = "Boise"
capitals["Maine"] = "Augusta"
</pre>
If the key isn't already present, this adds it and associates it with the given
value. If the key is already there, this just replaces its value.
@ -54,8 +60,9 @@ value. If the key is already there, this just replaces its value.
To find the value associated with some key, again you use your friend the
subscript operator:
:::wren
System.print(capitals["Idaho"]) //> Boise
<pre class="snippet">
System.print(capitals["Idaho"]) //> Boise
</pre>
If the key is present, this returns its value. Otherwise, it returns `null`. Of
course, `null` itself can also be used as a value, so seeing `null` here
@ -63,32 +70,36 @@ doesn't necessarily mean the key wasn't found.
To tell definitively if a key exists, you can call `containsKey()`:
:::wren
var belief = {"nihilism": null}
<pre class="snippet">
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`:
:::wren
System.print(capitals.count) //> 3
<pre class="snippet">
System.print(capitals.count) //> 3
</pre>
## Removing entries
To remove an entry from a map, call `remove()` and pass in the key for the
entry you want to delete:
:::wren
capitals.remove("Maine")
System.print(capitals.containsKey("Maine")) //> false
<pre class="snippet">
capitals.remove("Maine")
System.print(capitals.containsKey("Maine")) //> false
</pre>
If the key was found, this returns the value that was associated with it:
:::wren
System.print(capitals.remove("Georgia")) //> Atlanta
<pre class="snippet">
System.print(capitals.remove("Georgia")) //> Atlanta
</pre>
If the key wasn't in the map to begin with, `remove()` just returns `null`.
@ -97,40 +108,60 @@ If you want to remove *everything* from the map, like with [lists][], you call
[lists]: lists.html
:::wren
capitals.clear()
System.print(capitals.count) //> 0
<pre class="snippet">
capitals.clear()
System.print(capitals.count) //> 0
</pre>
## Iterating over the contents
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.
:::wren
var birds = {
"Arizona": "Cactus wren",
"Hawaii": "Nēnē",
"Ohio": "Northern Cardinal"
}
**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.
for (state in birds.keys) {
System.print("The state bird of " + state + " is " + birds[state])
}
<pre class="snippet">
var birds = {
"Arizona": "Cactus wren",
"Hawaii": "Nēnē",
"Ohio": "Northern Cardinal"
}
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.
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 = {
"Arizona": "Cactus wren",
"Hawaii": "Nēnē",
"Ohio": "Northern Cardinal"
}
for (state in birds.keys) {
System.print("The state bird of %(state) is " + birds[state])
}
</pre>
<br><hr>
<a class="right" href="method-calls.html">Method Calls &rarr;</a>
<a href="lists.html">&larr; Lists</a>

View File

@ -3,20 +3,23 @@
Wren is deeply object oriented, so most code consists of invoking methods on
objects, usually something like this:
:::wren
System.print("Heyoo!") //> Heyoo!
<pre class="snippet">
System.print("Heyoo!") //> Heyoo!
</pre>
You have a *receiver* expression (here `System`) followed by a `.`, then a name
(`print`) and an argument list in parentheses (`("Heyoo!")`). Multiple arguments
are separated by commas:
:::wren
list.insert(3, "item")
<pre class="snippet">
list.insert(3, "item")
</pre>
The argument list can also be empty:
:::wren
list.clear()
<pre class="snippet">
list.clear()
</pre>
The VM executes a method call like so:
@ -39,16 +42,19 @@ other only takes a maximum value and uses 0 as the minimum:
[random]: modules/random/random.html
:::wren
var random = Random.new()
random.int(3, 10)
random.int(4)
<pre class="snippet">
var random = Random.new()
random.int(3, 10)
random.int(4)
</pre>
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
@ -57,64 +63,75 @@ cases.
It's also faster to execute. Since we know how many arguments are passed at
compile time, we can compile this to directly call the right method and avoid
any "if I got two arguments do this..." logic.
any "if I got two arguments do this..." runtime work.
## Getters
Some methods exist to expose a stored or computed property of an object. These
are *getters* and have no parentheses:
:::wren
"string".count //> 6
(1..10).min //> 1
1.23.sin //> 0.9424888019317
[1, 2, 3].isEmpty //> false
Sometimes you have a method that doesn't need any parameters, but modifies the
object or has some other side effect. For those, it's better to use empty
parentheses:
:::wren
list.clear()
Also, when a method supports multiple arities, it's typical to include the `()`
in the zero-argument case to be consistent with the other versions:
Fiber.yield()
Fiber.yield("value")
<pre class="snippet">
"string".count //> 6
(1..10).min //> 1
1.23.sin //> 0.9424888019317
[1, 2, 3].isEmpty //> false
</pre>
A getter is *not* the same as a method with an empty argument list. The `()` is
part of the signature, so `count` and `count()` have different signatures.
Unlike Ruby's optional parentheses, Wren wants to make sure you call a getter
like a getter and a `()` method like a `()` method. These don't work:
:::wren
"string".count()
[1, 2, 3].clear
<pre class="snippet">
"string".count()
[1, 2, 3].clear
</pre>
If you're defining some member that doesn't need any parameters, you need to
decide if it should be a getter or a method with an empty `()` parameter list.
The general guidelines are:
* If it modifies the object or has some other side effect, make it a method:
<pre class="snippet">
list.clear()
</pre>
* If the method supports multiple arities, make the zero-parameter case a `()`
method to be consistent with the other versions:
<pre class="snippet">
Fiber.yield()
Fiber.yield("value")
</pre>
* Otherwise, it can probably be a getter.
## Setters
A getter lets an object expose a public "property" that you can *read*.
Likewise, a *setter* let you write to a property:
Likewise, a *setter* lets you write to a property:
:::wren
person.height = 74 // Grew up!
<pre class="snippet">
person.height = 74 // Grew up!
</pre>
Despite the `=`, this is just another syntax for a method call. From the
language's perspective, the above line is just a call to the `height=(_)`
method, passing in `74`.
method on `person`, passing in `74`.
Since the `=(_)` is in the setter's signature, an object can have both a getter
and setter with the same name without a collision. This way, you can have
read/write properties.
and setter with the same name without a collision. Defining both lets you
provide a read/write property.
## Operators
Wren has most of the same operators you know and love with the same precedence
and associativity. We have three prefix operators:
:::wren
! ~ -
<pre class="snippet">
! ~ -
</pre>
They are just method calls on their operand without any other arguments. An
expression like `!possible` means "call the `!` method on `possible`".
@ -122,8 +139,9 @@ expression like `!possible` means "call the `!` method on `possible`".
We also have a slew of infix operators&mdash;they have operands on both sides.
They are:
:::wren
* / % + - .. ... << >> < <= > >= == != & ^ | is
<pre class="snippet">
* / % + - .. ... << >> < <= > >= == != & ^ | is
</pre>
Like prefix operators, they are all funny ways of writing method calls. The left
operand is the receiver, and the right operand gets passed to it. So `a + b` is
@ -147,30 +165,34 @@ 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:
:::wren
list[0] // Get the first item in a list.
map["key"] // Get the value associated with "key".
<pre class="snippet">
list[0] // Get the first item in a list.
map["key"] // Get the value associated with "key".
</pre>
You know the refrain by now. In Wren, these are method calls. In the above
examples, the signature is `[_]`. Subscript operators may also take multiple
arguments, which is useful for things like multi-dimensional arrays:
:::wren
matrix[3, 5]
<pre class="snippet">
matrix[3, 5]
</pre>
These examples are subscript "getters", and there are also
corresponding *subscript setters*:
:::wren
list[0] = "item"
map["key"] = "value"
<pre class="snippet">
list[0] = "item"
map["key"] = "value"
</pre>
These are equivalent to method calls whose signature is `[_]=(_)` and whose
arguments are both the subscript (or subscripts) and the value on the right-hand
side.
<br><hr>
<a class="right" href="control-flow.html">Control Flow &rarr;</a>
<a href="maps.html">&larr; Maps</a>

View File

@ -25,8 +25,9 @@ When you run Wren and give it a file name to execute, the contents of that file
define the "main" module that execution starts at. To load and execute other
modules, you use an import statement:
:::wren
import "beverages" for Coffee, Tea
<pre class="snippet">
import "beverages" for Coffee, Tea
</pre>
This finds a module named "beverages" and executes its source code. Then, it
looks up two top-level variables, `Coffee` and `Tea` in *that* module and
@ -35,16 +36,29 @@ creates new variables in *this* module with their values.
This statement can appear anywhere a variable declaration is allowed, even
inside blocks:
:::wren
if (thirsty) {
import "beverages" for Coffee, Tea
}
<pre class="snippet">
if (thirsty) {
import "beverages" for Coffee, Tea
}
</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:
:::wren
import "some_imperative_code"
<pre class="snippet">
import "some_imperative_code"
</pre>
That's the basic idea. Now let's break it down into each of the steps it
performs:
@ -66,41 +80,43 @@ string is used to locate a blob of source code.
When the host application creates a new Wren VM, it provides a module loader
function:
:::c
WrenConfiguration config;
config.loadModuleFn = loadModule;
<pre class="snippet" data-lang="c">
WrenConfiguration config;
config.loadModuleFn = loadModule;
// Other configuration...
// Other configuration...
WrenVM* vm = wrenNewVM(&config);
WrenVM* vm = wrenNewVM(&config);
</pre>
That function has this signature:
:::c
char* WrenLoadModuleFn(WrenVM* vm, const char* name);
<pre class="snippet" data-lang="c">
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
The default little command-line VM that comes with Wren has a very simple
The [Wren CLI command-line tool](getting-started.html#using-the-wren-cli) has a very simple
lookup process. It appends the module name and ".wren" to the directory where
the main module was loaded and looks for that file. So, let's say you run:
:::bash
$ wren /code/my_program.wren
$ wren code/my_program.wren
And that main module has:
:::wren
import "some/module"
<pre class="snippet">
import "some/module"
</pre>
Then the command-line VM will try to find `/code/some/module.wren`. By
convention, forward slashes should be used as path separators, even on Windows,
@ -139,8 +155,9 @@ These are simply variables declared outside of any
These are visible to anything inside the module, but they can also be
*exported* and used by other modules. When Wren executes an import like:
:::wren
import "beverages" for Coffee, Tea
<pre class="snippet">
import "beverages" for Coffee, Tea
</pre>
First it runs the "beverages" module. Then it goes through each of the variable
names in the `for` clause. For each one, it looks for a top-level variable with
@ -163,19 +180,20 @@ Earlier, I described a program's set of modules as a tree. Of course, it's only
a *tree* of modules if there are no *shared imports*. But consider a program
like:
:::wren
// main.wren
import "a"
import "b"
<pre class="snippet">
// main.wren
import "a"
import "b"
// a.wren
import "shared"
// a.wren
import "shared"
// b.wren
import "shared"
// b.wren
import "shared"
// shared.wren
System.print("Shared!")
// shared.wren
System.print("Shared!")
</pre>
Here, "a" and "b" both want to use "shared". If "shared" defines some top-level
state, we only want a single copy of that in memory. To handle this, a module's
@ -208,23 +226,23 @@ it will be found in the registry and the cycle is short-circuited.
For example:
:::wren
// main.wren
import "a"
<pre class="snippet">
// main.wren
import "a"
// a.wren
System.print("start a")
import "b"
System.print("end a")
// a.wren
System.print("start a")
import "b"
System.print("end a")
// b.wren
System.print("start b")
import "a"
System.print("end b")
// b.wren
System.print("start b")
import "a"
System.print("end b")
</pre>
This program runs successfully and prints:
:::text
start a
start b
end b
@ -232,17 +250,18 @@ This program runs successfully and prints:
Where you have to be careful is binding variables. Consider:
:::wren
// main.wren
import "a"
<pre class="snippet">
// main.wren
import "a"
// a.wren
import "b" for B
var A = "a variable"
// a.wren
import "b" for B
var A = "a variable"
// b.wren
import "a" for A
var B = "b variable"
// b.wren
import "a" for A
var B = "b variable"
</pre>
The import of "a" in b.wren will fail here. If you trace the execution, you
get:
@ -257,17 +276,18 @@ defined yet since "a.wren" is still sitting on the `import "b" for B` line
before the declaration. To get this to work, you would need to move the
variable declaration above the import:
:::wren
// main.wren
import "a"
<pre class="snippet">
// main.wren
import "a"
// a.wren
var A = "a variable"
import "b" for B
// a.wren
var A = "a variable"
import "b" for B
// b.wren
import "a" for A
var B = "b variable"
// b.wren
import "a" for A
var B = "b variable"
</pre>
Now when we run it, we get:
@ -286,4 +306,19 @@ This sounds super hairy, but that's because cyclic dependencies are hairy in
general. The key point here is that Wren *can* handle them in the rare cases
where you need them.
## Exiting a module early
Although the `return` statement is normally used to exit from a [method](classes.html#methods) or a [function](functions.html), it can also be used from a module's top-level code to exit the module. For example, if the script consists of a single module, this code would exit the module (and therefore the script) early:
<pre class="snippet">
for (i in 1..2) {
if (i == 2) return
System.print(i) //> prints 1 but not 2
}
System.print(3) //> not reached
</pre>
Although it is not invalid to return a value, there is no way to access that value and it is therefore simply discarded.
<br><hr>
<a href="error-handling.html">&larr; Error Handling</a>

View File

@ -10,9 +10,10 @@ Boolean [values][]. There are two instances, `true` and `false`.
Returns the logical complement of the value.
:::wren
System.print(!true) //> false
System.print(!false) //> true
<pre class="snippet">
System.print(!true) //> false
System.print(!false) //> true
</pre>
### toString

View File

@ -12,18 +12,21 @@ The name of the class.
The superclass of this class.
:::wren
class Crustacean {}
class Crab is Crustacean {}
<pre class="snippet">
class Crustacean {}
class Crab is Crustacean {}
System.print(Crab.supertype) //> Crustacean
System.print(Crab.supertype) //> Crustacean
</pre>
A class with no explicit superclass implicitly inherits Object:
:::wren
System.print(Crustacean.supertype) //> Object
<pre class="snippet">
System.print(Crustacean.supertype) //> Object
</pre>
Object forms the root of the class hierarchy and has no supertype:
:::wren
System.print(Object.supertype) //> null
<pre class="snippet">
System.print(Object.supertype) //> null
</pre>

View File

@ -4,21 +4,37 @@ A lightweight coroutine. [Here][fibers] is a gentle introduction.
[fibers]: ../../concurrency.html
## Static Methods
### Fiber.**abort**(message)
Raises a runtime error with the provided message:
<pre class="snippet">
Fiber.abort("Something bad happened.")
</pre>
If the message is `null`, does nothing.
### Fiber.**current**
The currently executing fiber.
### Fiber.**new**(function)
Creates a new fiber that executes `function` in a separate coroutine when the
fiber is run. Does not immediately start running the fiber.
:::wren
var fiber = Fiber.new {
System.print("I won't get printed")
}
<pre class="snippet">
var fiber = Fiber.new {
System.print("I won't get printed")
}
</pre>
## Static Methods
`function` must be a function (an actual [Fn][] instance, not just an object
with a `call()` method) and it may only take zero or one parameters.
### Fiber.**current**
The currently executing fiber.
[fn]: fn.html
### Fiber.**suspend**()
@ -37,29 +53,31 @@ the fiber is resumed.
Pauses the current fiber and transfers control to the parent fiber. "Parent"
here means the last fiber that was started using `call` and not `transfer`.
:::wren
var fiber = Fiber.new {
System.print("Before yield")
Fiber.yield()
System.print("After yield")
}
<pre class="snippet">
var fiber = Fiber.new {
System.print("Before yield")
Fiber.yield()
System.print("After yield")
}
fiber.call() //> Before yield
System.print("After call") //> After call
fiber.call() //> After yield
fiber.call() //> Before yield
System.print("After call") //> After call
fiber.call() //> After yield
</pre>
When resumed, the parent fiber's `call()` method returns `null`.
If a yielded fiber is resumed by calling `call()` or `transfer()` with an
argument, `yield()` returns that value.
:::wren
var fiber = Fiber.new {
System.print(Fiber.yield()) //> value
}
<pre class="snippet">
var fiber = Fiber.new {
System.print(Fiber.yield()) //> value
}
fiber.call() // Run until the first yield.
fiber.call("value") // Resume the fiber.
fiber.call() // Run until the first yield.
fiber.call("value") // Resume the fiber.
</pre>
If it was resumed by calling `call()` or `transfer()` with no argument, it
returns `null`.
@ -68,78 +86,181 @@ If there is no parent fiber to return to, this exits the interpreter. This can
be useful to pause execution until the host application wants to resume it
later.
:::wren
Fiber.yield()
System.print("this does not get reached")
<pre class="snippet">
Fiber.yield()
System.print("this does not get reached")
</pre>
### Fiber.**yield**(value)
Similar to `Fiber.yield` but provides a value to return to the parent fiber's
`call`.
:::wren
var fiber = Fiber.new {
Fiber.yield("value")
}
<pre class="snippet">
var fiber = Fiber.new {
Fiber.yield("value")
}
System.print(fiber.call()) //> value
System.print(fiber.call()) //> value
</pre>
## Methods
### **call**()
Starts or resumes the fiber if it is in a paused state.
Starts or resumes the fiber if it is in a paused state. Equivalent to:
:::wren
var fiber = Fiber.new {
System.print("Fiber called")
Fiber.yield()
System.print("Fiber called again")
}
fiber.call() // Start it.
fiber.call() // Resume after the yield() call.
When the called fiber yields, control is transferred back to the fiber that
called it.
If the called fiber is resuming from a yield, the `yield()` method returns
`null` in the called fiber.
:::wren
var fiber = Fiber.new {
System.print(Fiber.yield())
}
fiber.call()
fiber.call() //> null
<pre class="snippet">
fiber.call(null)
</pre>
### **call**(value)
Invokes the fiber or resumes the fiber if it is in a paused state and sets
`value` as the returned value of the fiber's call to `yield`.
Start or resumes the fiber if it is in a paused state. If the fiber is being
started for the first time, and its function takes a parameter, `value` is
passed to it.
:::wren
var fiber = Fiber.new {
System.print(Fiber.yield())
}
<pre class="snippet">
var fiber = Fiber.new {|param|
System.print(param) //> begin
}
fiber.call()
fiber.call("value") //> value
fiber.call("begin")
</pre>
If the fiber is being resumed, `value` becomes the returned value of the fiber's
call to `yield`.
<pre class="snippet">
var fiber = Fiber.new {
System.print(Fiber.yield()) //> resume
}
fiber.call()
fiber.call("resume")
</pre>
### **error**
The error message that was passed when aborting the fiber, or `null` if the
fiber has not been aborted.
<pre class="snippet">
var fiber = Fiber.new {
123.badMethod
}
fiber.try()
System.print(fiber.error) //> Num does not implement method 'badMethod'.
</pre>
### **isDone**
Whether the fiber's main function has completed and the fiber can no longer be
run. This returns `false` if the fiber is currently running or has yielded.
### **try**()
Tries to run the fiber. If a runtime error occurs
in the called fiber, the error is captured and is returned as a string.
<pre class="snippet">
var fiber = Fiber.new {
123.badMethod
}
var error = fiber.try()
System.print("Caught error: " + error)
</pre>
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>

View File

@ -5,6 +5,8 @@ A first class function&mdash;an object that wraps an executable chunk of code.
[functions]: ../../functions.html
## Static Methods
### Fn.**new**(function)
Creates a new function from... `function`. Of course, `function` is already a
@ -12,10 +14,11 @@ function, so this really just returns the argument. It exists mainly to let you
create a "bare" function when you don't want to immediately pass it as a [block
argument](../functions.html#block-arguments) to some other method.
:::wren
var fn = Fn.new {
System.print("The body")
}
<pre class="snippet">
var fn = Fn.new {
System.print("The body")
}
</pre>
It is a runtime error if `function` is not a function.
@ -25,20 +28,22 @@ It is a runtime error if `function` is not a function.
The number of arguments the function requires.
:::wren
System.print(Fn.new {}.arity) //> 0
System.print(Fn.new {|a, b, c| a }.arity) //> 3
<pre class="snippet">
System.print(Fn.new {}.arity) //> 0
System.print(Fn.new {|a, b, c| a }.arity) //> 3
</pre>
### **call**(args...)
Invokes the function with the given arguments.
:::wren
var fn = Fn.new { |arg|
System.print(arg) //> Hello world
}
<pre class="snippet">
var fn = Fn.new { |arg|
System.print(arg) //> Hello world
}
fn.call("Hello world")
fn.call("Hello world")
</pre>
It is a runtime error if the number of arguments given is less than the arity
of the function. If more arguments are given than the function's arity they are

View File

@ -1,24 +1,4 @@
^title Module "core"
Because Wren is designed for [embedding in applications][embedding], its core
module is minimal and is focused on working with objects within Wren. For
stuff like file IO, graphics, etc., it is up to the host application to provide
interfaces for this.
All Wren source files automatically have access to the following classes:
* [Bool](bool.html)
* [Class](class.html)
* [Fiber](fiber.html)
* [Fn](fn.html)
* [List](list.html)
* [Map](map.html)
* [Null](null.html)
* [Num](num.html)
* [Object](object.html)
* [Range](range.html)
* [Sequence](sequence.html)
* [String](string.html)
* [System](system.html)
[embedding]: ../../embedding-api.html
<script type="text/javascript">
window.location = '../'
</script>

View File

@ -6,11 +6,35 @@ An indexable contiguous collection of elements. More details [here][lists].
[lists]: ../../lists.html
## Static Methods
### List.**filled**(size, element)
Creates a new list with `size` elements, all set to `element`.
It is a runtime error if `size` is not a non-negative integer.
### List.**new**()
Creates a new empty list. Equivalent to `[]`.
## Methods
### **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**()
@ -20,34 +44,48 @@ 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.
:::wren
var list = ["a", "b", "c", "d"]
list.insert(1, "e")
System.print(list) //> [a, e, b, c, d]
<pre class="snippet">
var list = ["a", "b", "c", "d"]
list.insert(1, "e")
System.print(list) //> [a, e, b, c, d]
</pre>
The `index` may be one past the last index in the list to append an element.
:::wren
var list = ["a", "b", "c"]
list.insert(3, "d")
System.print(list) //> [a, b, c, d]
<pre class="snippet">
var list = ["a", "b", "c"]
list.insert(3, "d")
System.print(list) //> [a, b, c, d]
</pre>
If `index` is negative, it counts backwards from the end of the list. It bases this on the length of the list *after* inserted the element, so that `-1` will append the element, not insert it before the last element.
:::wren
var list = ["a", "b"]
list.insert(-1, "d")
list.insert(-2, "c")
System.print(list) //> [a, b, c, d]
<pre class="snippet">
var list = ["a", "b"]
list.insert(-1, "d")
list.insert(-2, "c")
System.print(list) //> [a, b, c, d]
</pre>
Returns the inserted item.
:::wren
System.print(["a", "c"].insert(1, "b")) //> b
<pre class="snippet">
System.print(["a", "c"].insert(1, "b")) //> b
</pre>
It is a runtime error if the index is not an integer or is out of bounds.
@ -58,43 +96,132 @@ 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
from the end of the list where `-1` is the last element. All trailing elements
are shifted up to fill in where the removed element was.
:::wren
var list = ["a", "b", "c", "d"]
list.removeAt(1)
System.print(list) //> [a, c, d]
<pre class="snippet">
var list = ["a", "b", "c", "d"]
list.removeAt(1)
System.print(list) //> [a, c, d]
</pre>
Returns the removed item.
:::wren
System.print(["a", "b", "c"].removeAt(1)) //> b
<pre class="snippet">
System.print(["a", "b", "c"].removeAt(1)) //> b
</pre>
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
the end of the list where `-1` is the last element.
:::wren
var list = ["a", "b", "c"]
System.print(list[1]) //> b
<pre class="snippet">
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
Replaces the element at `index` with `item`. If `index` is negative, it counts
backwards from the end of the list where `-1` is the last element.
:::wren
var list = ["a", "b", "c"]
list[1] = "new"
System.print(list) //> [a, new, c]
<pre class="snippet">
var list = ["a", "b", "c"]
list[1] = "new"
System.print(list) //> [a, new, c]
</pre>
It is a runtime error if the index is not an integer or is out of bounds.
### **+**(other) operator
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>
### **\***(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>

View File

@ -1,6 +1,14 @@
^title Map Class
An associative collection that maps keys to values. More details [here](../maps.html).
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
@ -42,10 +50,11 @@ multiple times in the sequence.
Gets the value associated with `key` in the map. If `key` is not present in the
map, returns `null`.
:::wren
var map = {"george": "harrison", "ringo": "starr"}
System.print(map["ringo"]) //> starr
System.print(map["pete"]) //> null
<pre class="snippet">
var map = {"george": "harrison", "ringo": "starr"}
System.print(map["ringo"]) //> starr
System.print(map["pete"]) //> null
</pre>
### **[**key**]=**(value) operator
@ -55,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.

View File

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

View File

@ -9,18 +9,52 @@ Attempts to parse `value` as a decimal literal and return it as an instance of
It is a runtime error if `value` is not a string.
### Num.**infinity**
The value of &infin;.
### Num.**nan**
One value representing a NaN.
Provides a default NaN number suitable for the vm internal values.
### Num.**pi**
The value of &pi;.
### Num.**tau**
The value of &tau;. This is equivalent to ```2 * Num.pi```.
### Num.**largest**
The largest representable numeric value.
### Num.**smallest**
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**
The absolute value of the number.
:::wren
System.print( (-123).abs ) //> 123
<pre class="snippet">
System.print( (-123).abs ) //> 123
</pre>
### **acos**
@ -39,13 +73,18 @@ 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.
:::wren
System.print(1.5.ceil) //> 2
System.print((-3.2).ceil) //> -3
<pre class="snippet">
System.print(1.5.ceil) //> 2
System.print((-3.2).ceil) //> -3
</pre>
### **cos**
@ -55,25 +94,39 @@ The cosine of the number.
Rounds the number down to the nearest integer.
:::wren
System.print(1.5.floor) //> 1
System.print((-3.2).floor) //> -4
<pre class="snippet">
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.
:::wren
System.print(99999.isInfinity) //> false
System.print((1/0).isInfinity) //> true
<pre class="snippet">
System.print(99999.isInfinity) //> false
System.print((1/0).isInfinity) //> true
</pre>
### **isInteger**
Whether the number is an integer or has some fractional component.
:::wren
System.print(2.isInteger) //> true
System.print(2.3.isInteger) //> false
<pre class="snippet">
System.print(2.isInteger) //> true
System.print(2.3.isInteger) //> false
</pre>
### **isNan**
@ -81,6 +134,50 @@ Whether the number is [not a number](http://en.wikipedia.org/wiki/NaN). This is
`false` for normal number values and infinities, and `true` for the result of
`0/0`, the square root of a negative number, etc.
### **log**
The natural logarithm of the number. Returns `nan` if the base is negative.
### **log2**
The binary (base-2) logarithm of the number. Returns `nan` if the base is negative.
### **exp**
The exponential `e` (Eulers number) raised to the number. This: `eⁿ`.
### **min**(other)
Returns the minimum value when comparing this number and `other`.
### **max**(other)
Returns the maximum value when comparing this number and `other`.
### **clamp**(min, max)
Clamps a number into the range of `min` and `max`. If this number is less than min,
`min` is returned. If bigger than `max`, `max` is returned. Otherwise, the number
itself is returned.
### **pow**(power)
Raises this number (the base) to `power`. Returns `nan` if the base is negative.
### **round**
Rounds the number to the nearest integer.
<pre class="snippet">
System.print(1.5.round) //> 2
System.print((-3.2).round) //> -3
System.print((-3.7).round) //> -4
</pre>
### **sign**
The sign of the number, expressed as a -1, 1 or 0, for negative and positive numbers, and zero.
### **sin**
The sine of the number.
@ -93,13 +190,29 @@ 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.
:::wren
var a = 123
System.print(-a) //> -123
<pre class="snippet">
var a = 123
System.print(-a) //> -123
</pre>
### **-**(other), **+**(other), **/**(other), **\***(other) operators
@ -109,7 +222,10 @@ not a number. Wren doesn't roll with implicit conversions.
### **%**(denominator) operator
The floating-point remainder of this number divided by `denominator`.
Also known as mod or modulus.
The floating-point remainder of this number divided by `denominator`.
The returned value has the same sign as `this` (internally calls `fmod` from C).
It is a runtime error if `denominator` is not a number.
@ -136,7 +252,25 @@ It is a runtime error if `other` is not a number.
Performs bitwise 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 both inputs were `true`.
`true` only where the corresponding bits of one or both inputs were `true`.
It is a runtime error if `other` is not a number.
### **^**(other) operator
Performs bitwise exclusive or on the number. Both numbers are first converted to 32-bit unsigned values. The result is then a 32-bit unsigned number where each bit is `true` only where the corresponding bits of one (but not both) inputs were `true`. Each bit is therefore `false` if the corresponding bits of both inputs were either both `true` or both `false`.
It is a runtime error if `other` is not a number.
### **&lt;&lt;**(other) operator
Performs a bitwise left shift on the number. Internally, both numbers are first converted to 32-bit unsigned values and C's left shift operator is then applied to them.
It is a runtime error if `other` is not a number.
### **&gt;&gt;**(other) operator
Performs a bitwise right shift on the number. Internally, both numbers are first converted to 32-bit unsigned values and C's right shift operator is then applied to them.
It is a runtime error if `other` is not a number.
@ -145,19 +279,21 @@ It is a runtime error if `other` is not a number.
Creates a [Range](range.html) representing a consecutive range of numbers
from the beginning number to the ending number.
:::wren
var range = 1.2..3.4
System.print(range.min) //> 1.2
System.print(range.max) //> 3.4
System.print(range.isInclusive) //> true
<pre class="snippet">
var range = 1.2..3.4
System.print(range.min) //> 1.2
System.print(range.max) //> 3.4
System.print(range.isInclusive) //> true
</pre>
### **...**(other) operator
Creates a [Range](range.html) representing a consecutive range of numbers
from the beginning number to the ending number not including the ending number.
:::wren
var range = 1.2...3.4
System.print(range.min) //> 1.2
System.print(range.max) //> 3.4
System.print(range.isInclusive) //> false
<pre class="snippet">
var range = 1.2...3.4
System.print(range.min) //> 1.2
System.print(range.max) //> 3.4
System.print(range.isInclusive) //> false
</pre>

View File

@ -5,7 +5,7 @@
### **same**(obj1, obj2)
Returns `true` if *obj1* and *obj2* are the same. For [value
types](../values.html), this returns `true` if the objects have equivalent
types](../../values.html), this returns `true` if the objects have equivalent
state. In other words, numbers, strings, booleans, and ranges compare by value.
For all other objects, this returns `true` only if *obj1* and *obj2* refer to
@ -26,19 +26,20 @@ Returns `false`, since most objects are considered [true][].
### **==**(other) and **!=**(other) operators
Compares two objects using built-in equality. This compares [value
types](../values.html) by value, and all other objects are compared by
types](../../values.html) by value, and all other objects are compared by
identity&mdash;two objects are equal only if they are the exact same object.
### **is**(class) operator
Returns `true` if this object's class or one of its superclasses is `class`.
:::wren
System.print(123 is Num) //> true
System.print("s" is Num) //> false
System.print(null is String) //> false
System.print([] is List) //> true
System.print([] is Sequence) //> true
<pre class="snippet">
System.print(123 is Num) //> true
System.print("s" is Num) //> false
System.print(null is String) //> false
System.print([] is List) //> true
System.print([] is Sequence) //> true
</pre>
It is a runtime error if `class` is not a [Class][].

View File

@ -1,7 +1,7 @@
^title Range Class
A range defines a bounded range of values from a starting point to a possibly
exclusive endpoint. [Here](../range.html) is a friendly introduction.
exclusive endpoint. [Here](../../values.html#ranges) is a friendly introduction.
Extends [Sequence](sequence.html).
@ -12,44 +12,49 @@ Extends [Sequence](sequence.html).
The starting point of the range. A range may be backwards, so this can be
greater than [to].
:::wren
System.print((3..5).min) //> 3
System.print((4..2).min) //> 4
<pre class="snippet">
System.print((3..5).from) //> 3
System.print((4..2).from) //> 4
</pre>
### **to**
The endpoint of the range. If the range is inclusive, this value is included,
otherwise it is not.
:::wren
System.print((3..5).min) //> 5
System.print((4..2).min) //> 2
<pre class="snippet">
System.print((3..5).to) //> 5
System.print((4..2).to) //> 2
</pre>
### **min**
The minimum bound of the range. Returns either `from`, or `to`, whichever is
lower.
:::wren
System.print((3..5).min) //> 3
System.print((4..2).min) //> 2
<pre class="snippet">
System.print((3..5).min) //> 3
System.print((4..2).min) //> 2
</pre>
### **max**
The maximum bound of the range. Returns either `from`, or `to`, whichever is
greater.
:::wren
System.print((3..5).min) //> 5
System.print((4..2).min) //> 4
<pre class="snippet">
System.print((3..5).max) //> 5
System.print((4..2).max) //> 4
</pre>
### **isInclusive**
Whether or not the range includes `to`. (`from` is always included.)
:::wren
System.print((3..5).isInclusive) //> true
System.print((3...5).isInclusive) //> false
<pre class="snippet">
System.print((3..5).isInclusive) //> true
System.print((3...5).isInclusive) //> false
</pre>
### **iterate**(iterator), **iteratorValue**(iterator)

View File

@ -15,9 +15,10 @@ Iterates over the sequence, passing each element to the function `predicate`.
If it returns something [false](../control-flow.html#truth), stops iterating
and returns the value. Otherwise, returns `true`.
:::wren
System.print([1, 2, 3].all {|n| n > 2}) //> false
System.print([1, 2, 3].all {|n| n < 4}) //> true
<pre class="snippet">
System.print([1, 2, 3].all {|n| n > 2}) //> false
System.print([1, 2, 3].all {|n| n < 4}) //> true
</pre>
### **any**(predicate)
@ -29,9 +30,10 @@ returns that value. Otherwise, returns `false`.
[true]: ../../control-flow.html#truth
:::wren
System.print([1, 2, 3].any {|n| n < 1}) //> false
System.print([1, 2, 3].any {|n| n > 2}) //> true
<pre class="snippet">
System.print([1, 2, 3].any {|n| n < 1}) //> false
System.print([1, 2, 3].any {|n| n > 2}) //> true
</pre>
### **contains**(element)
@ -51,16 +53,18 @@ Returns the number of elements in the sequence that pass the `predicate`.
Iterates over the sequence, passing each element to the function `predicate`
and counting the number of times the returned value evaluates to `true`.
:::wren
System.print([1, 2, 3].count {|n| n > 2}) //> 1
System.print([1, 2, 3].count {|n| n < 4}) //> 3
<pre class="snippet">
System.print([1, 2, 3].count {|n| n > 2}) //> 1
System.print([1, 2, 3].count {|n| n < 4}) //> 3
</pre>
### **each**(function)
Iterates over the sequence, passing each element to the given `function`.
:::wren
["one", "two", "three"].each {|word| System.print(word) }
<pre class="snippet">
["one", "two", "three"].each {|word| System.print(word) }
</pre>
### **isEmpty**
@ -86,13 +90,14 @@ together into a single string.
Creates a new sequence that applies the `transformation` to each element in the
original sequence while it is iterated.
:::wren
var doubles = [1, 2, 3].map {|n| n * 2 }
for (n in doubles) {
System.print(n) //> 2
//> 4
//> 6
}
<pre class="snippet">
var doubles = [1, 2, 3].map {|n| n * 2 }
for (n in doubles) {
System.print(n) //> 2
//> 4
//> 6
}
</pre>
The returned sequence is *lazy*. It only applies the mapping when you iterate
over the sequence, and it does so by holding a reference to the original
@ -104,11 +109,12 @@ changes to the original sequence will be reflected in the mapped sequence.
To force eager evaluation, just call `.toList` on the result.
:::wren
var numbers = [1, 2, 3]
var doubles = numbers.map {|n| n * 2 }.toList
numbers.add(4)
System.print(doubles) //> [2, 4, 6]
<pre class="snippet">
var numbers = [1, 2, 3]
var doubles = numbers.map {|n| n * 2 }.toList
numbers.add(4)
System.print(doubles) //> [2, 4, 6]
</pre>
### **reduce**(function)
@ -125,14 +131,32 @@ It is a runtime error to call this on an empty sequence.
Similar to above, but uses `seed` for the initial value of the accumulator. If
the sequence is empty, returns `seed`.
### **skip**(count)
Creates a new sequence that skips the first `count` elements of the original
sequence.
The returned sequence is *lazy*. The first `count` elements are only skipped
once you start to iterate the returned sequence. Changes to the original
sequence will be reflected in the filtered sequence.
### **take**(count)
Creates a new sequence that iterates only the first `count` elements of the
original sequence.
The returned sequence is *lazy*. Changes to the original sequence will be
reflected in the filtered sequence.
### **toList**
Creates a [list][] containing all the elements in the sequence.
[list]: list.html
:::wren
System.print((1..3).toList) //> [1, 2, 3]
<pre class="snippet">
System.print((1..3).toList) //> [1, 2, 3]
</pre>
If the sequence is already a list, this creates a copy of it.
@ -144,13 +168,14 @@ that pass the `predicate`.
During iteration, each element in the original sequence is passed to the
function `predicate`. If it returns `false`, the element is skipped.
:::wren
var odds = (1..6).where {|n| n % 2 == 1 }
for (n in odds) {
System.print(n) //> 1
//> 3
//> 5
}
<pre class="snippet">
var odds = (1..6).where {|n| n % 2 == 1 }
for (n in odds) {
System.print(n) //> 1
//> 3
//> 5
}
</pre>
The returned sequence is *lazy*. It only applies the filtering when you iterate
over the sequence, and it does so by holding a reference to the original
@ -163,8 +188,9 @@ sequence.
To force eager evaluation, just call `.toList` on the result.
:::wren
var numbers = [1, 2, 3, 4, 5, 6]
var odds = numbers.where {|n| n % 2 == 1 }.toList
numbers.add(7)
System.print(odds) //> [1, 3, 5]
<pre class="snippet">
var numbers = [1, 2, 3, 4, 5, 6]
var odds = numbers.where {|n| n % 2 == 1 }.toList
numbers.add(7)
System.print(odds) //> [1, 3, 5]
</pre>

View File

@ -26,17 +26,28 @@ counting them as you go.
Because counting code points is relatively slow, the indexes passed to string
methods are *byte* offsets, not *code point* offsets. When you do:
:::wren
someString[3]
<pre class="snippet">
someString[3]
</pre>
That means "get the code point starting at *byte* three", not "get the third
code point in the string". This sounds scary, but keep in mind that the methods
on strings *return* byte indexes too. So, for example, this does what you want:
:::wren
var metalBand = "Fäcëhämmër"
var hPosition = metalBand.indexOf("h")
System.print(metalBand[hPosition]) //> h
<pre class="snippet">
var metalBand = "Fäcëhämmër"
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
@ -51,12 +62,23 @@ ignores any UTF-8 encoding and works directly at the byte level.
Creates a new string containing the UTF-8 encoding of `codePoint`.
:::wren
String.fromCodePoint(8225) //> ‡
<pre class="snippet">
String.fromCodePoint(8225) //> ‡
</pre>
It is a runtime error if `codePoint` is not an integer between `0` and
`0x10ffff`, inclusive.
### String.**fromByte**(byte)
Creates a new string containing the single byte `byte`.
<pre class="snippet">
String.fromByte(255) //> <20>
</pre>
It is a runtime error if `byte` is not an integer between `0` and `0xff`, inclusive.
## Methods
### **bytes**
@ -66,8 +88,9 @@ the string and ignore any UTF-8 encoding. In addition to the normal sequence
methods, the returned object also has a subscript operator that can be used to
directly index bytes.
:::wren
System.print("hello".bytes[1]) //> 101 (for "e")
<pre class="snippet">
System.print("hello".bytes[1]) //> 101 (for "e")
</pre>
The `count` method on the returned sequence returns the number of bytes in the
string. Unlike `count` on the string itself, it does not have to iterate over
@ -80,17 +103,19 @@ code points of the string *as numbers*. Iteration and subscripting work similar
to the string itself. The difference is that instead of returning
single-character strings, this returns the numeric code point values.
:::wren
var string = "(ᵔᴥᵔ)"
System.print(string.codePoints[0]) //> 40 (for "(")
System.print(string.codePoints[4]) //> 7461 (for "ᴥ")
<pre class="snippet">
var string = "(ᵔᴥᵔ)"
System.print(string.codePoints[0]) //> 40 (for "(")
System.print(string.codePoints[4]) //> 7461 (for "ᴥ")
</pre>
If the byte at `index` does not begin a valid UTF-8 sequence, or the end of the
string is reached before the sequence is complete, returns `-1`.
:::wren
var string = "(ᵔᴥᵔ)"
System.print(string.codePoints[2]) //> -1 (in the middle of "ᵔ")
<pre class="snippet">
var string = "(ᵔᴥᵔ)"
System.print(string.codePoints[2]) //> -1 (in the middle of "ᵔ")
</pre>
### **contains**(other)
@ -120,34 +145,123 @@ Returns the index of the first byte matching `search` in the string or `-1` if
It is a runtime error if `search` is not a string.
### **indexOf**(search, start)
Returns the index of the first byte matching `search` in the string or `-1` if
`search` was not found, starting at byte offset `start`. The `start` offset can
also be negative, which will be offset relative to end of the string instead.
Searches forward, from the offset to the end of the string.
It is a runtime error if `search` is not a string or `start` is not an integer
index within the string's byte length.
### **iterate**(iterator), **iteratorValue**(iterator)
Implements the [iterator protocol](../control-flow.html#the-iterator-protocol)
for iterating over the *code points* in the string:
Implements the [iterator protocol][] for iterating over the *code points* in the
string:
:::wren
var codePoints = []
for (c in "(ᵔᴥᵔ)") {
codePoints.add(c)
}
[iterator protocol]: ../../control-flow.html#the-iterator-protocol
System.print(codePoints) //> [(, ᵔ, ᴥ, ᵔ, )]
<pre class="snippet">
var codePoints = []
for (c in "(ᵔᴥᵔ)") {
codePoints.add(c)
}
System.print(codePoints) //> [(, ᵔ, ᴥ, ᵔ, )]
</pre>
If the string contains any bytes that are not valid UTF-8, this iterates over
those too, one byte at a time.
### **replace**(old, swap)
Returns a new string with all occurrences of `old` replaced with `swap`.
<pre class="snippet">
var string = "abc abc abc"
System.print(string.replace(" ", "")) //> abcabcabc
</pre>
### **split**(separator)
Returns a list of one or more strings separated by `separator`.
<pre class="snippet">
var string = "abc abc abc"
System.print(string.split(" ")) //> [abc, abc, abc]
</pre>
It is a runtime error if `separator` is not a string or is an empty string.
### **startsWith**(prefix)
Checks if the string starts with `prefix`.
It is a runtime error if `prefix` is not a string.
### **trim**()
Returns a new string with whitespace removed from the beginning and end of this
string. "Whitespace" is space, tab, carriage return, and line feed characters.
<pre class="snippet">
System.print(" \nstuff\r\t".trim()) //> stuff
</pre>
### **trim**(chars)
Returns a new string with all code points in `chars` removed from the beginning
and end of this string.
<pre class="snippet">
System.print("ᵔᴥᵔᴥᵔbearᵔᴥᴥᵔᵔ".trim("ᵔᴥ")) //> bear
</pre>
### **trimEnd**()
Like `trim()` but only removes from the end of the string.
<pre class="snippet">
System.print(" \nstuff\r\t".trimEnd()) //> " \nstuff"
</pre>
### **trimEnd**(chars)
Like `trim()` but only removes from the end of the string.
<pre class="snippet">
System.print("ᵔᴥᵔᴥᵔbearᵔᴥᴥᵔᵔ".trimEnd("ᵔᴥ")) //> ᵔᴥᵔᴥᵔbear
</pre>
### **trimStart**()
Like `trim()` but only removes from the beginning of the string.
<pre class="snippet">
System.print(" \nstuff\r\t".trimStart()) //> "stuff\r\t"
</pre>
### **trimStart**(chars)
Like `trim()` but only removes from the beginning of the string.
<pre class="snippet">
System.print("ᵔᴥᵔᴥᵔbearᵔᴥᴥᵔᵔ".trimStart("ᵔᴥ")) //> bearᵔᴥᴥᵔᵔ
</pre>
### **+**(other) operator
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`.
@ -160,8 +274,9 @@ Check if the string is not equal to `other`.
Returns a string containing the code point starting at byte `index`.
:::wren
System.print("ʕ•ᴥ•ʔ"[5]) //> ᴥ
<pre class="snippet">
System.print("ʕ•ᴥ•ʔ"[5]) //> ᴥ
</pre>
Since `ʕ` is two bytes in UTF-8 and `•` is three, the fifth byte points to the
bear's nose.
@ -169,8 +284,9 @@ bear's nose.
If `index` points into the middle of a UTF-8 sequence or at otherwise invalid
UTF-8, this returns a one-byte string containing the byte at that index:
:::wren
System.print("I ♥ NY"[3]) //> (one-byte string [153])
<pre class="snippet">
System.print("I ♥ NY"[3]) //> (one-byte string [153])
</pre>
It is a runtime error if `index` is greater than the number of bytes in the
string.

View File

@ -24,24 +24,32 @@ Prints a single newline to the console.
Prints `object` to the console followed by a newline. If not already a string,
the object is converted to a string by calling `toString` on it.
:::wren
System.print("I like bananas") //> I like bananas
<pre class="snippet">
System.print("I like bananas") //> I like bananas
</pre>
### System.**printAll**(sequence)
Iterates over `sequence` and prints each element, then prints a single newline
at the end. Each element is converted to a string by calling `toString` on it.
:::wren
System.printAll([1, [2, 3], 4]) //> 1[2, 3]4
<pre class="snippet">
System.printAll([1, [2, 3], 4]) //> 1[2, 3]4
</pre>
### System.**write**(object)
Prints a single value to the console, but does not print a newline character
afterwards. Converts the value to a string by calling `toString` on it.
:::wren
System.write(4 + 5) //> 9
<pre class="snippet">
System.write(4 + 5) //> 9
</pre>
In the above example, the result of `4 + 5` is printed, and then the prompt is
printed on the same line because no newline character was printed afterwards.
### System.**writeAll**(sequence)
Iterates over `sequence` and prints each element, but does not print a newline
character afterwards. Each element is converted to a string by calling `toString` on it.

View File

@ -3,6 +3,9 @@
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<script type="application/javascript" src="../../prism.js" data-manual></script>
<script type="application/javascript" src="../../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../../prism.css" />
<link rel="stylesheet" type="text/css" href="../../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
@ -20,9 +23,9 @@
</header>
<div class="page">
<nav class="big">
<a href="../../"><img src="../../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Modules</a></li>
<li><a href="./">core</a></li>
<li><a href="../">Back to Modules</a></li>
</ul>
<section>
<h2>core classes</h2>
@ -86,10 +89,10 @@
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/munificent/wren">on GitHub</a>
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>

View File

@ -1,49 +1,33 @@
^title Modules
Because Wren can be used both as an embedded scripting language, and as a
general purpose programming language run from the command line, the definition
of a "built-in" module is a little complicated. They are organized into three
categories:
## Core
There is one core module. It is built directly into the VM and is implicitly
imported by every other module. It contains the classes for the objects built
directly into the language itself: [numbers][], [strings][], etc.
[numbers]: core/num.html
[strings]: core/string.html
The core module is always available and can't be removed.
* [core](core)
## Optional
Optional modules are available in the command line Wren interpreter. When you
embed Wren in your own host application, you can also include them too. They are
written in Wren and C, but have no external dependencies, so including them in
your application doesn't force you to bring in any other third-party code.
At the same time, they aren't *needed* by the VM itself to function, so you can
disable some or all of them if you want to keep your app as small and
constrained as possible.
There are a couple of optional modules:
* [meta](meta)
* [random](random)
## CLI
The CLI modules are only available in the standalone command-line Wren
interpreter. They are deeply tied to [libuv][], each other, and other internals
of the command-line app, so can't be separated out and pulled into host
applications that want to embed Wren.
[libuv]: http://libuv.org
* [io](io)
* [process](process)
* [scheduler](scheduler)
* [timer](timer)
^title Modules
Wren comes with two kinds of modules, the core module (built-in),
and a few optional modules that the host embedding Wren can enable.
## Core module
The core module is built directly into the VM and is implicitly
imported by every other module. You don't need to `import` anything to use it.
It contains objects and types for the language itself like [numbers][] and [strings][].
Because Wren is designed for [embedding in applications][embedding], its core
module is minimal and is focused on working with objects within Wren. For
stuff like file IO, graphics, etc., it is up to the host application to provide
interfaces for this.
[numbers]: core/num.html
[strings]: core/string.html
## Optional modules
Optional modules are available in the Wren project, but whether they are included is up to the host.
They are written in Wren and C, with no external dependencies, so including them in
your application is as easy as a simple compile flag.
Since they aren't *needed* by the VM itself to function, you can
disable some or all of them, so check if your host has them available.
So far there are a few optional modules:
* [meta docs](meta)
* [random docs](random)

View File

@ -1,5 +1,9 @@
^title Module "meta"
**TODO**
This module enables Wren to do certain kinds of meta-programming.
It is an optional module. You can omit it from your application by setting the preprocessor constant `WREN_OPT_META` to `0`.
It contains a single class:
* [Meta](meta.html)

View File

@ -1,7 +1,145 @@
^title Meta Class
**TODO**
This class contains static methods to list a module's top-level variables and to compile Wren expressions and source code into closures (i.e. [functions](functions.html)) at runtime.
## Methods
It must be imported from the [meta](meta.html) module:
**TODO**
<pre class="snippet">
import "meta" for Meta
</pre>
## Static Methods
### **getModuleVariables**(module)
Returns a list of all module level variables defined or visible in `module`.
This includes any variables explicitly imported from other modules or implicitly imported from the built-in modules. For example if we create this module:
<pre class="snippet">
/* module.wren */
var M = 1
</pre>
and then import it into this module:
<pre class="snippet">
/* get_mod_vars.wren */
import "meta" for Meta
import "./module" for M
var v = 42
var f = Fn.new {
var g = 2
}
class C {}
System.print(Meta.getModuleVariables("./get_mod_vars"))
var w = 43 // still returned even though defined later
</pre>
the output when the latter module is run is:
<pre class="snippet">
[Object, Class, Object metaclass, Bool, Fiber, Fn, Null, Num, Sequence, MapSequence, SkipSequence, TakeSequence, WhereSequence, List, String, StringByteSequence, StringCodePointSequence, Map, MapKeySequence, MapValueSequence, MapEntry, Range, System, Meta, M, v, f, C, w]
</pre>
Notice that `g` is not included in this list as it is a local variable rather than a module variable.
It is a runtime error if `module` is not a string or cannot be found.
### **eval**(source)
Compiles Wren source code into a closure and then executes the closure automatically.
It is a runtime error if `source` is not a string.
It is also an error if the source code cannot be compiled though the compilation errors themselves are not printed.
For example:
<pre class="snippet">
import "meta" for Meta
var a = 2
var b = 3
var source = """
var c = a * b
System.print(c)
"""
Meta.eval(source) //> 6
</pre>
### **compileExpression**(expression)
Compiles a Wren expression into a closure and then returns the closure. It does not execute it.
The closure returns the value of the expression.
It is a runtime error if `expression` is not a string.
Prints any compilation errors - in which event the closure will be null - but does not throw an error.
For example:
<pre class="snippet">
import "meta" for Meta
var d = 4
var e = 5
var expression = "d * e"
var closure = Meta.compileExpression(expression)
System.print(closure.call()) //> 20
</pre>
### **compile**(source)
Compiles Wren source code into a closure and then returns the closure. It does not execute it.
It is a runtime error if `source` is not a string.
Prints any compilation errors - in which event the closure will be null - but does not throw an error.
For example:
<pre class="snippet">
import "meta" for Meta
/* Enum creates an enum with any number of read-only static members.
Members are assigned in order an initial integer value (often 0), incremented by 1 each time.
The enum has:
1. static property getters for each member,
2. a static 'startsFrom' property, and
3. a static 'members' property which returns a list of its members as strings.
*/
class Enum {
// Creates a class for the Enum (with an underscore after the name to avoid duplicate definition)
// and returns a reference to it.
static create(name, members, startsFrom) {
if (name.type != String || name == "") Fiber.abort("Name must be a non-empty string.")
if (members.isEmpty) Fiber.abort("An enum must have at least one member.")
if (startsFrom.type != Num || !startsFrom.isInteger) {
Fiber.abort("Must start from an integer.")
}
name = name + "_"
var s = "class %(name) {\n"
for (i in 0...members.count) {
var m = members[i]
s = s + " static %(m) { %(i + startsFrom) }\n"
}
var mems = members.map { |m| "\"%(m)\"" }.join(", ")
s = s + " static startsFrom { %(startsFrom) }\n"
s = s + " static members { [%(mems)] }\n}\n"
s = s + "return %(name)"
return Meta.compile(s).call()
}
}
var Fruits = Enum.create("Fruits", ["orange", "apple", "banana", "lemon"], 0)
System.print(Fruits.banana) //> 2
System.print(Fruits.startsFrom) //> 0
System.print(Fruits.members) //> [orange, apple, banana, lemon]
</pre>

View File

@ -3,6 +3,9 @@
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<script type="application/javascript" src="../../prism.js" data-manual></script>
<script type="application/javascript" src="../../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../../prism.css" />
<link rel="stylesheet" type="text/css" href="../../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
@ -20,9 +23,10 @@
</header>
<div class="page">
<nav class="big">
<a href="../../"><img src="../../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Modules</a></li>
<li><a href="./">meta</a></li>
<li><a href="../">Back to Modules</a></li>
<li><a href="./">meta module</a></li>
</ul>
<section>
<h2>meta classes</h2>
@ -62,10 +66,10 @@
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/munificent/wren">on GitHub</a>
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>

View File

@ -1,5 +0,0 @@
^title Module "process"
The process module exposes a single class for working with operating processes.
* [Process](process.html)

View File

@ -13,8 +13,9 @@ numbers.
It must be imported from the [random][] module:
:::wren
<pre class="snippet">
import "random" for Random
</pre>
[random]: ../
@ -24,8 +25,9 @@ It must be imported from the [random][] module:
Creates a new generator whose state is seeded based on the current time.
:::wren
var random = Random.new()
<pre class="snippet">
var random = Random.new()
</pre>
### Random.**new**(seed)
@ -34,9 +36,10 @@ number, or a non-empty sequence of numbers. If the sequnce has more than 16
elements, only the first 16 are used. If it has fewer, the elements are cycled
to generate 16 seed values.
:::wren
Random.new(12345)
Random.new("appleseed".codePoints)
<pre class="snippet">
Random.new(12345)
Random.new("appleseed".codePoints)
</pre>
## Methods
@ -45,54 +48,59 @@ to generate 16 seed values.
Returns a floating point value between 0.0 and 1.0, including 0.0, but excluding
1.0.
:::wren
var random = Random.new(12345)
System.print(random.float()) //> 0.53178795980617
System.print(random.float()) //> 0.20180515043262
System.print(random.float()) //> 0.43371948658705
<pre class="snippet">
var random = Random.new(12345)
System.print(random.float()) //> 0.53178795980617
System.print(random.float()) //> 0.20180515043262
System.print(random.float()) //> 0.43371948658705
</pre>
### **float**(end)
Returns a floating point value between 0.0 and `end`, including 0.0 but
excluding `end`.
:::wren
var random = Random.new(12345)
System.print(random.float(0)) //> 0
System.print(random.float(100)) //> 20.180515043262
System.print(random.float(-100)) //> -43.371948658705
<pre class="snippet">
var random = Random.new(12345)
System.print(random.float(0)) //> 0
System.print(random.float(100)) //> 20.180515043262
System.print(random.float(-100)) //> -43.371948658705
</pre>
### **float**(start, end)
Returns a floating point value between `start` and `end`, including `start` but
excluding `end`.
:::wren
var random = Random.new(12345)
System.print(random.float(3, 4)) //> 3.5317879598062
System.print(random.float(-10, 10)) //> -5.9638969913476
System.print(random.float(-4, 2)) //> -1.3976830804777
<pre class="snippet">
var random = Random.new(12345)
System.print(random.float(3, 4)) //> 3.5317879598062
System.print(random.float(-10, 10)) //> -5.9638969913476
System.print(random.float(-4, 2)) //> -1.3976830804777
</pre>
### **int**(end)
Returns an integer between 0 and `end`, including 0 but excluding `end`.
:::wren
var random = Random.new(12345)
System.print(random.int(1)) //> 0
System.print(random.int(10)) //> 2
System.print(random.int(-50)) //> -22
<pre class="snippet">
var random = Random.new(12345)
System.print(random.int(1)) //> 0
System.print(random.int(10)) //> 2
System.print(random.int(-50)) //> -22
</pre>
### **int**(start, end)
Returns an integer between `start` and `end`, including `start` but excluding
`end`.
:::wren
var random = Random.new(12345)
System.print(random.int(3, 4)) //> 3
System.print(random.int(-10, 10)) //> -6
System.print(random.int(-4, 2)) //> -2
<pre class="snippet">
var random = Random.new(12345)
System.print(random.int(3, 4)) //> 3
System.print(random.int(-10, 10)) //> -6
System.print(random.int(-4, 2)) //> -2
</pre>
### **sample**(list)
@ -114,11 +122,12 @@ It is an error if `count` is greater than the number of elements in the list.
Randomly shuffles the elements in `list`. The items are randomly re-ordered in
place.
:::wren
var random = Random.new(12345)
var list = (1..5).toList
random.shuffle(list)
System.print(list) //> [3, 2, 4, 1, 5]
<pre class="snippet">
var random = Random.new(12345)
var list = (1..5).toList
random.shuffle(list)
System.print(list) //> [3, 2, 4, 1, 5]
</pre>
Uses the Fisher-Yates algorithm to ensure that all permutations are chosen
with equal probability.

View File

@ -3,6 +3,9 @@
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<script type="application/javascript" src="../../prism.js" data-manual></script>
<script type="application/javascript" src="../../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../../prism.css" />
<link rel="stylesheet" type="text/css" href="../../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
@ -20,9 +23,10 @@
</header>
<div class="page">
<nav class="big">
<a href="../../"><img src="../../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Modules</a></li>
<li><a href="./">random</a></li>
<li><a href="../">Back to Modules</a></li>
<li><a href="./">random module</a></li>
</ul>
<section>
<h2>random classes</h2>
@ -62,10 +66,10 @@
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/munificent/wren">on GitHub</a>
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>

View File

@ -1,5 +0,0 @@
^title Module "scheduler"
**TODO**
* [Scheduler](scheduler.html)

View File

@ -1,7 +0,0 @@
^title Scheduler Class
**TODO**
## Methods
**TODO**

View File

@ -3,6 +3,9 @@
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>{title} &ndash; Wren</title>
<script type="application/javascript" src="../prism.js" data-manual></script>
<script type="application/javascript" src="../wren.js"></script>
<link rel="stylesheet" type="text/css" href="../prism.css" />
<link rel="stylesheet" type="text/css" href="../style.css" />
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
@ -20,10 +23,26 @@
</header>
<div class="page">
<nav class="big">
<a href="../"><img src="../wren.svg" class="logo"></a>
<ul>
<li><a href="../">Back to Wren</a></li>
</ul>
<section>
<h2>core</h2>
<h2>core classes</h2>
<ul>
<li><a href="core">core</a></li>
<li><a href="core/bool.html">Bool</a></li>
<li><a href="core/class.html">Class</a></li>
<li><a href="core/fiber.html">Fiber</a></li>
<li><a href="core/fn.html">Fn</a></li>
<li><a href="core/list.html">List</a></li>
<li><a href="core/map.html">Map</a></li>
<li><a href="core/null.html">Null</a></li>
<li><a href="core/num.html">Num</a></li>
<li><a href="core/object.html">Object</a></li>
<li><a href="core/range.html">Range</a></li>
<li><a href="core/sequence.html">Sequence</a></li>
<li><a href="core/string.html">String</a></li>
<li><a href="core/system.html">System</a></li>
</ul>
</section>
<section>
@ -33,27 +52,29 @@
<li><a href="random">random</a></li>
</ul>
</section>
<section>
<h2>cli</h2>
<ul>
<li><a href="io">io</a></li>
<li><a href="process">process</a></li>
<li><a href="scheduler">scheduler</a></li>
<li><a href="timer">timer</a></li>
</ul>
</section>
</nav>
<nav class="small">
<table>
<tr>
<td><h2>core</h2></td>
<td><h2>core classes</h2></td>
<td><h2>optional</h2></td>
<td><h2>cli</h2></td>
</tr>
<tr>
<td>
<ul>
<li><a href="core">core</a></li>
<li><a href="core/bool.html">Bool</a></li>
<li><a href="core/class.html">Class</a></li>
<li><a href="core/fiber.html">Fiber</a></li>
<li><a href="core/fn.html">Fn</a></li>
<li><a href="core/list.html">List</a></li>
<li><a href="core/map.html">Map</a></li>
<li><a href="core/null.html">Null</a></li>
<li><a href="core/num.html">Num</a></li>
<li><a href="core/object.html">Object</a></li>
<li><a href="core/range.html">Range</a></li>
<li><a href="core/sequence.html">Sequence</a></li>
<li><a href="core/string.html">String</a></li>
<li><a href="core/system.html">System</a></li>
</ul>
</td>
<td>
@ -62,14 +83,6 @@
<li><a href="random">random</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="io">io</a></li>
<li><a href="process">process</a></li>
<li><a href="scheduler">scheduler</a></li>
<li><a href="timer">timer</a></li>
</ul>
</td>
</tr>
</table>
</nav>
@ -82,10 +95,10 @@
<div class="page">
<div class="main-column">
<p>Wren lives
<a href="https://github.com/munificent/wren">on GitHub</a>
<a href="https://github.com/wren-lang/wren">on GitHub</a>
&mdash; Made with &#x2764; by
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
</p>
<div class="main-column">
</div>

View File

@ -1,5 +0,0 @@
^title Module "timer"
**TODO**
* [Timer](timer.html)

View File

@ -1,7 +0,0 @@
^title Timer Class
**TODO**
## Methods
**TODO**

View File

@ -92,7 +92,7 @@ languages benchmarked, including Wren, because Mike Pall is a robot from the
future.
The benchmark harness and programs are
[here](https://github.com/munificent/wren/tree/master/test/benchmark).
[here](https://github.com/wren-lang/wren/tree/main/test/benchmark).
## Why is Wren fast?

View File

@ -32,36 +32,42 @@ prototypes) that classes are more usable.
Here's an example of that kind of object-oriented programming in Lua:
:::lua
Account = {}
Account.__index = Account
<pre class="snippet">
-- account.lua
function Account.create(balance)
local acnt = {} -- our new object
setmetatable(acnt,Account) -- make Account handle lookup
acnt.balance = balance -- initialize our object
return acnt
end
Account = {}
Account.__index = Account
function Account:withdraw(amount)
self.balance = self.balance - amount
end
function Account.create(balance)
local acnt = {} -- our new object
setmetatable(acnt,Account) -- make Account handle lookup
acnt.balance = balance -- initialize our object
return acnt
end
-- create and use an Account
account = Account.create(1000)
account:withdraw(100)
function Account:withdraw(amount)
self.balance = self.balance - amount
end
-- create and use an Account
account = Account.create(1000)
account:withdraw(100)
</pre>
Here's the same example in Wren:
:::wren
class Account {
construct new(balance) { _balance = balance }
withdraw(amount) { _balance = _balance - amount }
}
<pre class="snippet">
//account.wren
// create and use an Account
var account = Account.new(1000)
account.withdraw(100)
class Account {
construct new(balance) { _balance = balance }
withdraw(amount) { _balance = _balance - amount }
}
// create and use an Account
var account = Account.new(1000)
account.withdraw(100)
</pre>
Classes have a reputation for complexity because most of the widely used
languages with them are quite complex: C++, Java, C#, Ruby, and Python. I hope
@ -98,8 +104,9 @@ Register-based VMs have big instructions (usually 32 bits) that contain both an
opcode and a couple of numbers indicating where in the stack the operands can
be found. This is cool because it means, that, for example, this Lua statement:
:::lua
a = b + c
<pre class="snippet">
a = b + c
</pre>
Can be a single bytecode instruction. In a stack-based language, it would be
four&mdash;push `b`, push `c`, add, store `a`. (Though note that in both cases

View File

@ -0,0 +1,51 @@
function withLineNumbers(highlight, options = {}) {
const opts = Object.assign({ class: "codejar-linenumbers", wrapClass: "codejar-wrap", width: "35px" }, options);
let lineNumbers;
return function (editor) {
highlight(editor);
if (!lineNumbers) {
lineNumbers = init(editor, opts);
}
const code = editor.textContent || "";
const linesCount = code.replace(/\n+$/, "\n").split("\n").length + 1;
let text = "";
for (let i = 1; i < linesCount; i++) {
text += `${i}\n`;
}
lineNumbers.innerText = text;
};
}
function init(editor, opts) {
const css = getComputedStyle(editor);
const wrap = document.createElement("div");
wrap.className = opts.wrapClass;
wrap.style.position = "relative";
const lineNumbers = document.createElement("div");
lineNumbers.className = opts.class;
wrap.appendChild(lineNumbers);
// Add own styles
lineNumbers.style.position = "absolute";
lineNumbers.style.top = "0px";
lineNumbers.style.left = "0px";
lineNumbers.style.bottom = "0px";
lineNumbers.style.width = opts.width;
lineNumbers.style.overflow = "hidden";
lineNumbers.style.backgroundColor = "rgba(255, 255, 255, 0.05)";
lineNumbers.style.color = "#fff";
lineNumbers.style.setProperty("mix-blend-mode", "difference");
// Copy editor styles
lineNumbers.style.fontFamily = css.fontFamily;
lineNumbers.style.fontSize = css.fontSize;
lineNumbers.style.lineHeight = css.lineHeight;
lineNumbers.style.paddingTop = css.paddingTop;
lineNumbers.style.paddingLeft = css.paddingLeft;
lineNumbers.style.borderTopLeftRadius = css.borderTopLeftRadius;
lineNumbers.style.borderBottomLeftRadius = css.borderBottomLeftRadius;
// Tweak editor styles
editor.style.paddingLeft = `calc(${opts.width} + ${lineNumbers.style.paddingLeft})`;
editor.style.whiteSpace = "pre";
// Swap editor with a wrap
editor.parentNode.insertBefore(wrap, editor);
wrap.appendChild(editor);
return lineNumbers;
}

401
doc/site/static/codejar.js Normal file
View File

@ -0,0 +1,401 @@
function CodeJar(editor, highlight, opt = {}) {
const options = Object.assign({ tab: "\t" }, opt);
let listeners = [];
let history = [];
let at = -1;
let focus = false;
let callback;
let prev; // code content prior keydown event
let isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
editor.setAttribute("contentEditable", isFirefox ? "true" : "plaintext-only");
editor.setAttribute("spellcheck", "false");
editor.style.outline = "none";
editor.style.overflowWrap = "break-word";
editor.style.overflowY = "auto";
editor.style.resize = "vertical";
editor.style.whiteSpace = "pre-wrap";
highlight(editor);
const debounceHighlight = debounce(() => {
const pos = save();
highlight(editor);
restore(pos);
}, 30);
let recording = false;
const shouldRecord = (event) => {
return !isUndo(event) && !isRedo(event)
&& event.key !== "Meta"
&& event.key !== "Control"
&& event.key !== "Alt"
&& !event.key.startsWith("Arrow");
};
const debounceRecordHistory = debounce((event) => {
if (shouldRecord(event)) {
recordHistory();
recording = false;
}
}, 300);
const on = (type, fn) => {
listeners.push([type, fn]);
editor.addEventListener(type, fn);
};
on("keydown", event => {
if (event.defaultPrevented)
return;
prev = toString();
handleNewLine(event);
handleTabCharacters(event);
handleJumpToBeginningOfLine(event);
handleSelfClosingCharacters(event);
handleUndoRedo(event);
if (shouldRecord(event) && !recording) {
recordHistory();
recording = true;
}
});
on("keyup", event => {
if (event.defaultPrevented)
return;
if (event.isComposing)
return;
if (prev !== toString())
debounceHighlight();
debounceRecordHistory(event);
if (callback)
callback(toString());
});
on("focus", _event => {
focus = true;
});
on("blur", _event => {
focus = false;
});
on("paste", event => {
recordHistory();
handlePaste(event);
recordHistory();
if (callback)
callback(toString());
});
function save() {
const s = window.getSelection();
const pos = { start: 0, end: 0, dir: undefined };
visit(editor, el => {
if (el === s.anchorNode && el === s.focusNode) {
pos.start += s.anchorOffset;
pos.end += s.focusOffset;
pos.dir = s.anchorOffset <= s.focusOffset ? "->" : "<-";
return "stop";
}
if (el === s.anchorNode) {
pos.start += s.anchorOffset;
if (!pos.dir) {
pos.dir = "->";
}
else {
return "stop";
}
}
else if (el === s.focusNode) {
pos.end += s.focusOffset;
if (!pos.dir) {
pos.dir = "<-";
}
else {
return "stop";
}
}
if (el.nodeType === Node.TEXT_NODE) {
if (pos.dir != "->")
pos.start += el.nodeValue.length;
if (pos.dir != "<-")
pos.end += el.nodeValue.length;
}
});
return pos;
}
function restore(pos) {
const s = window.getSelection();
let startNode, startOffset = 0;
let endNode, endOffset = 0;
if (!pos.dir)
pos.dir = "->";
if (pos.start < 0)
pos.start = 0;
if (pos.end < 0)
pos.end = 0;
// Flip start and end if the direction reversed
if (pos.dir == "<-") {
const { start, end } = pos;
pos.start = end;
pos.end = start;
}
let current = 0;
visit(editor, el => {
if (el.nodeType !== Node.TEXT_NODE)
return;
const len = (el.nodeValue || "").length;
if (current + len >= pos.start) {
if (!startNode) {
startNode = el;
startOffset = pos.start - current;
}
if (current + len >= pos.end) {
endNode = el;
endOffset = pos.end - current;
return "stop";
}
}
current += len;
});
// If everything deleted place cursor at editor
if (!startNode)
startNode = editor;
if (!endNode)
endNode = editor;
// Flip back the selection
if (pos.dir == "<-") {
[startNode, startOffset, endNode, endOffset] = [endNode, endOffset, startNode, startOffset];
}
s.setBaseAndExtent(startNode, startOffset, endNode, endOffset);
}
function beforeCursor() {
const s = window.getSelection();
const r0 = s.getRangeAt(0);
const r = document.createRange();
r.selectNodeContents(editor);
r.setEnd(r0.startContainer, r0.startOffset);
return r.toString();
}
function afterCursor() {
const s = window.getSelection();
const r0 = s.getRangeAt(0);
const r = document.createRange();
r.selectNodeContents(editor);
r.setStart(r0.endContainer, r0.endOffset);
return r.toString();
}
function handleNewLine(event) {
if (event.key === "Enter") {
const before = beforeCursor();
const after = afterCursor();
let [padding] = findPadding(before);
let newLinePadding = padding;
// If last symbol is "{" ident new line
if (before[before.length - 1] === "{") {
newLinePadding += options.tab;
}
if (isFirefox) {
preventDefault(event);
insert("\n" + newLinePadding);
}
else {
// Normal browsers
if (newLinePadding.length > 0) {
preventDefault(event);
insert("\n" + newLinePadding);
}
}
// Place adjacent "}" on next line
if (newLinePadding !== padding && after[0] === "}") {
const pos = save();
insert("\n" + padding);
restore(pos);
}
}
}
function handleSelfClosingCharacters(event) {
const open = `([{'"`;
const close = `)]}'"`;
const codeAfter = afterCursor();
if (close.includes(event.key) && codeAfter.substr(0, 1) === event.key) {
const pos = save();
preventDefault(event);
pos.start = ++pos.end;
restore(pos);
}
else if (open.includes(event.key)) {
const pos = save();
preventDefault(event);
const text = event.key + close[open.indexOf(event.key)];
insert(text);
pos.start = ++pos.end;
restore(pos);
}
}
function handleTabCharacters(event) {
if (event.key === "Tab") {
preventDefault(event);
if (event.shiftKey) {
const before = beforeCursor();
let [padding, start,] = findPadding(before);
if (padding.length > 0) {
const pos = save();
// Remove full length tab or just remaining padding
const len = Math.min(options.tab.length, padding.length);
restore({ start, end: start + len });
document.execCommand("delete");
pos.start -= len;
pos.end -= len;
restore(pos);
}
}
else {
insert(options.tab);
}
}
}
function handleJumpToBeginningOfLine(event) {
if (event.key === "ArrowLeft" && event.metaKey) {
preventDefault(event);
const before = beforeCursor();
let [padding, start, end] = findPadding(before);
if (before.endsWith(padding)) {
if (event.shiftKey) {
const pos = save();
restore({ start, end: pos.end }); // Select from line start.
}
else {
restore({ start, end: start }); // Jump to line start.
}
}
else {
if (event.shiftKey) {
const pos = save();
restore({ start: end, end: pos.end }); // Select from beginning of text.
}
else {
restore({ start: end, end }); // Jump to beginning of text.
}
}
}
}
function handleUndoRedo(event) {
if (isUndo(event)) {
preventDefault(event);
at--;
const record = history[at];
if (record) {
editor.innerHTML = record.html;
restore(record.pos);
}
if (at < 0)
at = 0;
}
if (isRedo(event)) {
preventDefault(event);
at++;
const record = history[at];
if (record) {
editor.innerHTML = record.html;
restore(record.pos);
}
if (at >= history.length)
at--;
}
}
function recordHistory() {
if (!focus)
return;
const html = editor.innerHTML;
const pos = save();
const lastRecord = history[at];
if (lastRecord) {
if (lastRecord.html === html
&& lastRecord.pos.start === pos.start
&& lastRecord.pos.end === pos.end)
return;
}
at++;
history[at] = { html, pos };
history.splice(at + 1);
const maxHistory = 300;
if (at > maxHistory) {
at = maxHistory;
history.splice(0, 1);
}
}
function handlePaste(event) {
preventDefault(event);
const text = (event.originalEvent || event).clipboardData.getData("text/plain");
const pos = save();
insert(text);
highlight(editor);
restore({ start: pos.end + text.length, end: pos.end + text.length });
}
function visit(editor, visitor) {
const queue = [];
if (editor.firstChild)
queue.push(editor.firstChild);
let el = queue.pop();
while (el) {
if (visitor(el) === "stop")
break;
if (el.nextSibling)
queue.push(el.nextSibling);
if (el.firstChild)
queue.push(el.firstChild);
el = queue.pop();
}
}
function isCtrl(event) {
return event.metaKey || event.ctrlKey;
}
function isUndo(event) {
return isCtrl(event) && !event.shiftKey && event.code === "KeyZ";
}
function isRedo(event) {
return isCtrl(event) && event.shiftKey && event.code === "KeyZ";
}
function insert(text) {
text = text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
document.execCommand("insertHTML", false, text);
}
function debounce(cb, wait) {
let timeout = 0;
return (...args) => {
clearTimeout(timeout);
timeout = window.setTimeout(() => cb(...args), wait);
};
}
function findPadding(text) {
// Find beginning of previous line.
let i = text.length - 1;
while (i >= 0 && text[i] !== "\n")
i--;
i++;
// Find padding of the line.
let j = i;
while (j < text.length && /[ \t]/.test(text[j]))
j++;
return [text.substring(i, j) || "", i, j];
}
function toString() {
return editor.textContent || "";
}
function preventDefault(event) {
event.preventDefault();
}
return {
updateOptions(options) {
options = Object.assign(Object.assign({}, options), options);
},
updateCode(code) {
editor.textContent = code;
highlight(editor);
},
onUpdate(cb) {
callback = cb;
},
toString,
destroy() {
for (let [type, fn] of listeners) {
editor.removeEventListener(type, fn);
}
},
};
}

89
doc/site/static/lzstring.min.js vendored Normal file
View File

@ -0,0 +1,89 @@
// Minified from https://github.com/microsoft/TypeScript-Website/blob/e9d8f66f6b8be2dda06737d3686dcb795749dff2/packages/sandbox/src/vendor/lzstring.min.js
// Original license reproduced below:
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
// Version 2, December 2004
//
// Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
//
// Everyone is permitted to copy and distribute verbatim or modified
// copies of this license document, and changing it is allowed as long
// as the name is changed.
//
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
//
// 0. You just DO WHAT THE FUCK YOU WANT TO.
//
var LZString=(function(){function o(o,r){if(!t[o]){t[o]={}
for(var n=0;n<o.length;n++)t[o][o.charAt(n)]=n}
return t[o][r]}
var r=String.fromCharCode,n='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',e='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$',t={},i={compressToBase64:function(o){if(null==o)return''
var r=i._compress(o,6,function(o){return n.charAt(o)})
switch(r.length%4){default:case 0:return r
case 1:return r+'==='
case 2:return r+'=='
case 3:return r+'='}},decompressFromBase64:function(r){return null==r?'':''==r?null:i._decompress(r.length,32,function(e){return o(n,r.charAt(e))})},compressToUTF16:function(o){return null==o?'':i._compress(o,15,function(o){return r(o+32)})+' '},decompressFromUTF16:function(o){return null==o?'':''==o?null:i._decompress(o.length,16384,function(r){return o.charCodeAt(r)-32})},compressToUint8Array:function(o){for(var r=i.compress(o),n=new Uint8Array(2*r.length),e=0,t=r.length;t>e;e++){var s=r.charCodeAt(e);(n[2*e]=s>>>8),(n[2*e+1]=s%256)}
return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o)
for(var n=new Array(o.length / 2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1]
var s=[]
return(n.forEach(function(o){s.push(r(o))}),i.decompress(s.join('')))},compressToEncodedURIComponent:function(o){return null==o?'':i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?'':''==r?null:((r=r.replace(/ /g,'+')),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return''
var e,t,i,s={},p={},u='',c='',a='',l=2,f=3,h=2,d=[],m=0,v=0
for(i=0;i<o.length;i+=1)
if(((u=o.charAt(i)),Object.prototype.hasOwnProperty.call(s,u)||((s[u]=f++),(p[u]=!0)),(c=a+u),Object.prototype.hasOwnProperty.call(s,c)))
a=c
else{if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)(m<<=1),v==r-1?((v=0),d.push(n(m)),(m=0)):v++
for(t=a.charCodeAt(0),e=0;8>e;e++)
(m=(m<<1)|(1&t)),v==r-1?((v=0),d.push(n(m)),(m=0)):v++,(t>>=1)}else{for(t=1,e=0;h>e;e++)
(m=(m<<1)|t),v==r-1?((v=0),d.push(n(m)),(m=0)):v++,(t=0)
for(t=a.charCodeAt(0),e=0;16>e;e++)
(m=(m<<1)|(1&t)),v==r-1?((v=0),d.push(n(m)),(m=0)):v++,(t>>=1)}
l--,0==l&&((l=Math.pow(2,h)),h++),delete p[a]}else
for(t=s[a],e=0;h>e;e++)
(m=(m<<1)|(1&t)),v==r-1?((v=0),d.push(n(m)),(m=0)):v++,(t>>=1)
l--,0==l&&((l=Math.pow(2,h)),h++),(s[c]=f++),(a=String(u))}
if(''!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)(m<<=1),v==r-1?((v=0),d.push(n(m)),(m=0)):v++
for(t=a.charCodeAt(0),e=0;8>e;e++)
(m=(m<<1)|(1&t)),v==r-1?((v=0),d.push(n(m)),(m=0)):v++,(t>>=1)}else{for(t=1,e=0;h>e;e++)
(m=(m<<1)|t),v==r-1?((v=0),d.push(n(m)),(m=0)):v++,(t=0)
for(t=a.charCodeAt(0),e=0;16>e;e++)
(m=(m<<1)|(1&t)),v==r-1?((v=0),d.push(n(m)),(m=0)):v++,(t>>=1)}
l--,0==l&&((l=Math.pow(2,h)),h++),delete p[a]}else
for(t=s[a],e=0;h>e;e++)
(m=(m<<1)|(1&t)),v==r-1?((v=0),d.push(n(m)),(m=0)):v++,(t>>=1)
l--,0==l&&((l=Math.pow(2,h)),h++)}
for(t=2,e=0;h>e;e++)
(m=(m<<1)|(1&t)),v==r-1?((v=0),d.push(n(m)),(m=0)):v++,(t>>=1)
for(;;){if(((m<<=1),v==r-1)){d.push(n(m))
break}
v++}
return d.join('')},decompress:function(o){return null==o?'':''==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v='',w=[],A={val:e(0),position:n,index:1}
for(i=0;3>i;i+=1)f[i]=i
for(p=0,c=Math.pow(2,2),a=1;a!=c;)
(u=A.val&A.position),(A.position>>=1),0==A.position&&((A.position=n),(A.val=e(A.index++))),(p|=(u>0?1:0)*a),(a<<=1)
switch((t=p)){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)
(u=A.val&A.position),(A.position>>=1),0==A.position&&((A.position=n),(A.val=e(A.index++))),(p|=(u>0?1:0)*a),(a<<=1)
l=r(p)
break
case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)
(u=A.val&A.position),(A.position>>=1),0==A.position&&((A.position=n),(A.val=e(A.index++))),(p|=(u>0?1:0)*a),(a<<=1)
l=r(p)
break
case 2:return''}
for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return''
for(p=0,c=Math.pow(2,m),a=1;a!=c;)
(u=A.val&A.position),(A.position>>=1),0==A.position&&((A.position=n),(A.val=e(A.index++))),(p|=(u>0?1:0)*a),(a<<=1)
switch((l=p)){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)
(u=A.val&A.position),(A.position>>=1),0==A.position&&((A.position=n),(A.val=e(A.index++))),(p|=(u>0?1:0)*a),(a<<=1);(f[d++]=r(p)),(l=d-1),h--
break
case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)
(u=A.val&A.position),(A.position>>=1),0==A.position&&((A.position=n),(A.val=e(A.index++))),(p|=(u>0?1:0)*a),(a<<=1);(f[d++]=r(p)),(l=d-1),h--
break
case 2:return w.join('')}
if((0==h&&((h=Math.pow(2,m)),m++),f[l]))v=f[l]
else{if(l!==d)return null
v=s+s.charAt(0)}
w.push(v),(f[d++]=s+v.charAt(0)),h--,(s=v),0==h&&((h=Math.pow(2,m)),m++)}},}
return i})();'function'==typeof define&&define.amd?define(function(){return LZString}):'undefined'!=typeof module&&null!=module&&(module.exports=LZString)

142
doc/site/static/prism.css Normal file
View File

@ -0,0 +1,142 @@
/* PrismJS 1.20.0
https://prismjs.com/download.html#themes=prism&languages=clike+c+lua */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.token.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

6
doc/site/static/prism.js Normal file

File diff suppressed because one or more lines are too long

607
doc/site/static/style.css Normal file
View File

@ -0,0 +1,607 @@
:root {
--header-h: 8em;
--header: "Sanchez", helvetica, arial, sans-serif;
--subheader: "Lato", helvetica, arial, sans-serif;
--code: "Source Code Pro", Menlo, Monaco, Consolas, monospace;
--body: "Source Sans Pro", georgia, serif;
--code-bg: hsl(210, 0%, 99%);
--dark: hsl(210, 10%, 25%);
--darker: hsl(210, 20%, 10%);
--light: hsl(0, 0%, 100%);
--code-color: hsl(210, 20%, 30%);
--text: #333333;
--gray-5: whitesmoke;
--gray-10: #ebebec;
--gray-20: #d7d8da;
--gray-30: #c3c5c7;
--gray-50: #9c9fa2;
--gray-60: #888c90;
--gray-80: #60666a;
--link: hsl(200, 60%, 50%);
--link-dark: hsl(210, 60%, 20%);
--link-hover: hsl(210, 100%, 80%);
--link: hsl(200, 60%, 50%);
--module-link-dark: hsl(160, 60%, 25%);
--module-link-hover: hsl(130, 70%, 70%);
--module-link: hsl(150, 70%, 40%);
}
* {
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body, code, h1, h2, h3, p, pre, html {
margin: 0;
padding: 0;
}
html {
height: 100%;
min-height: 100%;
}
body {
background-color: var(--light);
color: var(--text);
font: 16px/25px var(--body);
height: 100%;
min-height: 100%;
}
hr {
display: block;
height: 1px;
border: 0;
border-top: 1px solid var(--gray-5);
margin: 1em 0;
padding: 0;
}
blockquote {
color: var(--gray-50);
margin: 0;
max-width: 24em;
margin-left: 0.5em;
}
date {
color: var(--gray-80);
}
.page {
margin: 0 auto;
width: 800px;
min-height: calc(100% - 16.75em);
}
/* Clear contents.*/
.page::after {
content: "";
display: table;
clear: both;
}
.main-column, main {
position: relative;
width: 560px;
}
.logo {
height: 7em;
position: relative;
margin: auto;
display: block;
left: -1em;
margin-bottom: 2em;
}
header {
background: var(--light);
border-bottom: solid 1px var(--gray-10);
}
header h1 {
position: absolute;
left: -8px;
top: calc((var(--header-h) / 3) - 1.15em);
padding: 0;
font-weight: 400;
font-size: 48px;
font-family: "Sanchez", helvetica, arial, sans-serif;
letter-spacing: 2px;
}
header h2 {
position: absolute;
left: 0;
top: calc(var(--header-h) + 0.5em);
padding: 0;
font: 500 13px var(--subheader);
text-transform: uppercase;
letter-spacing: 2px;
color: var(--gray-50);
}
header a {
color: var(--gray-80);
}
header a:hover {
color: var(--link-hover);
}
header .page {
height: var(--header-h);
}
nav {
float: right;
width: 160px;
margin-top: 2em;
}
nav h2 {
color: var(--gray-80);
font: 500 13px var(--subheader);
text-transform: uppercase;
letter-spacing: 2px;
margin: 0;
}
nav ul {
padding: 0;
margin: 6px 0 20px 0;
}
nav li {
font: 17px var(--body);
color: var(--gray-30);
list-style-type: none;
margin: 0 0 4px 0;
}
nav.small {
/*Only show the mobile navigation on small screens.*/
display: none;
float: none;
width: 100%;
padding: 16px 0 0 0;
margin: 0;
background: var(--gray-10);
}
nav.small div:not(table) { padding-left: 1em; }
nav.small table {
width: 100%;
border-collapse: separate;
border-spacing: 16px 0;
}
nav.small h2 {
margin: 16px 0 0 0;
padding: 0 0 1px 0;
border-bottom: solid 1px var(--gray-20);
}
h1 {
padding-top: 30px;
font: 500 36px/60px var(--header);
color: var(--link);
}
h2 {
font-weight: 500;
font-size: 24px;
font-family: var(--header);
margin: 24px 0 0 0;
color: var(--link);
}
h2 code {
border: none;
background: inherit;
color: inherit;
font-size: 24px;
}
h3 {
font: 20px var(--body);
margin: 24px 0 0 0;
color: var(--link);
}
h3 code {
border: none;
background: inherit;
color: inherit;
font-size: 20px;
}
a {
color: var(--link);
text-decoration: none;
transition: color 0.2s, text-shadow 0.2s;
outline: none;
cursor: pointer;
}
main {
margin-top: 2em;
float: left;
}
main .intro {
border-bottom: solid 1px var(--gray-10);
margin-bottom: -0.5em;
}
main h2 {
display: block;
position: relative;
max-width: 16em;
}
a:hover {
color: var(--link-dark);
}
.header-anchor {
color: var(--light);
}
h2:hover > .header-anchor,
h3:hover > .header-anchor {
color: var(--gray-10);
}
h2:hover > .header-anchor:hover,
h3:hover > .header-anchor:hover {
color: var(--link-dark);
}
p {
margin: 10px 0;
}
p + p {
margin-top: 20px;
}
code, pre {
background-color: var(--code-bg) !important;
color: var(--code-color) !important;
font-family: var(--code) !important;
font-size: 13px !important;
}
code {
white-space: pre;
}
pre {
border-radius: 2px;
border: solid 1px hsl(200, 20%, 88%);
overflow: auto;
white-space: pre-wrap;
padding: 1em;
}
footer {
margin-top: 4em;
padding: 20px 0 40px 0;
font: 14px var(--body);
background: var(--dark);
color: var(--gray-20);
border-top: solid 1px var(--darker);
text-align: center;
text-shadow: 0 1px 1px var(--darker);
}
footer a {
color: var(--link-hover);
}
footer a:hover {
color: var(--link);
}
.right {
float: right;
}
/* Have a different primary color for the module docs.*/
body.module header a {
color: var(--gray-80);
}
body.module header a:hover {
color: var(--module-link-hover);
}
body.module a {
color: var(--module-link);
}
body.module a:hover {
color: var(--module-link-dark);
}
body.module .header-anchor {
color: var(--light);
}
body.module footer a {
color: var(--module-link-hover);
}
body.module footer a:hover {
color: var(--module-link);
}
body.module main h1 { color: var(--module-link); }
body.module main h2 { color: var(--module-link); }
body.module main h3 { color: var(--module-link); }
body.module main h2:hover > .header-anchor:hover,
body.module main h3:hover > .header-anchor:hover {
color: var(--module-link-dark);
}
/* Try related stuff */
.buttons {
z-index: 2;
right: 0;
display: block;
position: absolute;
font-size: 1.3em;
letter-spacing: 0.05em;
margin-top: -1.25em;
}
.button {
margin-right: 0.25em;
display: inline-block;
text-align: right;
}
#examples {
margin-bottom: 1em;
}
#try-area {
margin-bottom: 1em;
}
#try-area p {
margin-left: 0.2em;
color: var(--gray-50);
font-size: 0.9em;
margin-bottom: -0.1em;
}
#try-output {
scrollbar-width: thin;
white-space: pre;
overflow-x: auto;
font-family: var(--code);
font-size: 1em;
line-height: 1.25em;
}
#try-result {
display: block;
margin-top: 1.4em;
position: absolute;
right: 0;
}
#try-result.error {
color: #c00;
font-weight: bold;
}
span.token.error {
color: #c00;
}
.codejar-linenumbers {
background-color: #f9f9f9 !important;
border-left: solid 1px hsl(200, 20%, 88%);
border-top: solid 1px hsl(200, 20%, 88%);
border-bottom: solid 1px hsl(200, 20%, 88%);
color: #d9d9d9 !important;
mix-blend-mode: normal !important;
width: 2.5em !important;
text-align: right !important;
padding-left: 0 !important;
padding-right: 0.5em !important;
}
#try-code {
scrollbar-width: thin;
border-radius: 0.01em;
border: solid 1px hsl(200, 20%, 88%);
font-family: 'Source Code Pro', monospace;
font-size: 14px;
font-weight: 400;
height: auto;
letter-spacing: normal;
line-height: 1.5em;
padding: 1em;
tab-size: 2;
}
table {
width: 100%;
border-collapse: collapse;
}
tr {
margin: 0;
padding: 0;
vertical-align: top;
}
th, td {
font-size: 14px;
line-height: 20px;
text-align: left;
}
/* Bar charts on the performance page.*/
table.chart {
margin: 4px 0 0 0;
padding: 5px 0 5px 25px;
}
table.chart td, th {
line-height: 14px;
margin: 0;
padding: 1px 0;
}
table.chart th {
font-size: 14px;
width: 100px;
}
table.chart .chart-bar {
display: inline-block;
font: 13px var(--body);
color: var(--light);
background: var(--link);
border-bottom: solid 1px var(--link-dark);
text-align: right;
border-radius: 2px;
}
table.chart .chart-bar.wren {
background: #1d5176;
border-bottom: solid 1px var(--link-dark);
}
/* Precedence table on expressions page.*/
table.precedence th {
font: 500 11px var(--subheader);
text-transform: uppercase;
letter-spacing: 1px;
color: var(--gray-60);
padding: 6px 0;
border-bottom: solid 1px var(--gray-10);
}
table.precedence td {
padding: 3px 0;
border-bottom: solid 1px var(--gray-10);
}
/*
Sourced from https://github.com/microsoft/TypeScript-Website/blob/e9d8f66f6b8be2dda06737d3686dcb795749dff2/packages/typescriptlang-org/src/templates/play.scss#L916-L943.
Licensed under the MIT license https://github.com/microsoft/TypeScript-Website/blob/e9d8f66f6b8be2dda06737d3686dcb795749dff2/LICENSE-CODE.
*/
#copied-popup {
top: 0;
left: 0;
right: 0;
bottom: 0;
position: fixed;
z-index: 100;
display: flex;
justify-content: center;
align-items: center;
pointer-events: none;
}
#copied-popup p {
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 20px;
font-size: 1.5rem;
border-radius: 1em;
padding: 0.5em 1.5em;
opacity: 0;
transition: opacity 0.1s ease-in-out;
/* Help Safari with blurred text */
transform: translateZ(0);
}
@media only screen and (max-width: 839px) {
/* 36 pixel columns.*/
.page { width: 720px; }
nav { width: 144px; }
.main-column, main { width: 504px; }
}
@media only screen and (max-width: 759px) {
/* 32 pixel columns.*/
.page { width: 640px; }
nav { width: 128px; }
.main-column, main { width: 448px; }
}
@media only screen and (max-width: 679px) {
/* 28 pixel columns.*/
.page { width: 560px; }
nav { width: 112px; }
.main-column, main { width: 392px; }
header h2 {
font-size: 12px;
letter-spacing: 1px;
}
}
@media only screen and (max-width: 639px) {
.page { width: 100%; }
.buttons { margin-right: 1em; }
#try-result { margin-right: 1.5em; }
/* Switch to the mobile navigation.*/
nav.big { display: none; }
nav.small { display: block; }
nav.small div:not(table) { padding-left: 1em; }
.main-column, main {
padding: 0 20px;
width: 100%;
}
header h1 {
position: relative;
top: 10px;
left: 0;
text-align: center;
}
header h2 {
position: relative;
top: 0;
right: 0;
text-align: center;
font-size: 13px;
letter-spacing: 2px;
}
main {
float: none;
width: 100%;
}
pre {
font-size: 13px;
}
footer {
padding: 20px 20px 40px 20px;
}
}

102
doc/site/static/wren.js Normal file
View File

@ -0,0 +1,102 @@
window.onload = function() {
var blocks = document.querySelectorAll('pre.snippet')
blocks.forEach((element) => {
var lang = 'lua'
var input_lang = element.getAttribute('data-lang')
if(input_lang) lang = input_lang
var code = document.createElement('code');
code.setAttribute('class', ' language-'+lang);
code.innerHTML = element.innerHTML;
element.innerHTML = '';
element.append(code)
});
Prism.highlightAll();
var try_code = document.querySelector("#try-code")
if(try_code) {
var jar_options = { tab: ' '.repeat(2) }
var jar = CodeJar(try_code, withLineNumbers(Prism.highlightElement), jar_options)
var output = document.querySelector("#try-output")
var result = document.querySelector("#try-result")
Module.print = function(text) { output.innerText += text + "\n"; }
Module.printErr = function(text) { output.innerText += text + "\n"; }
var run = document.querySelector("#try-run")
var share = document.querySelector("#share")
var hello = document.querySelector("#try-hello")
var fractal = document.querySelector("#try-fractal")
var loop = document.querySelector("#try-loop")
var copiedPopup = document.querySelector("#copied-popup p")
var compile = Module.cwrap('wren_compile', 'number', ['string'])
var set_input = (content) => {
output.innerText = '...';
result.removeAttribute('class');
result.innerText = 'no errors';
jar.updateCode(content);
}
share.onclick = (e) => {
var code = jar.toString()
var compressed = LZString.compressToEncodedURIComponent(code)
var url = location.protocol + "//" + location.host + location.pathname + "?code=" + compressed
navigator.clipboard.writeText(url).then(
() => {
copiedPopup.style.opacity = "1"
setTimeout(() => {
copiedPopup.style.opacity = ""
}, 1000)
},
(e) => console.error(e)
)
}
run.onclick = (e) => {
console.log("run")
output.setAttribute('ready', '');
output.innerText = '';
var res = compile(jar.toString())
var message = "no errors!"
result.removeAttribute('class');
if(res == 1) { //WREN_RESULT_COMPILE_ERROR
message = "Compile error!"
result.setAttribute('class', 'error');
} else if(res == 2) { //WREN_RESULT_RUNTIME_ERROR
message = "Runtime error!"
result.setAttribute('class', 'error');
}
result.innerText = message;
console.log(result);
}
hello.onclick = (e) => { set_input('System.print("hello wren")') }
loop.onclick = (e) => { set_input(`for (i in 1..10) System.print("Counting up %(i)")`); }
fractal.onclick = (e) => {
set_input(`for (yPixel in 0...24) {
var y = yPixel / 12 - 1
for (xPixel in 0...80) {
var x = xPixel / 30 - 2
var x0 = x
var y0 = y
var iter = 0
while (iter < 11 && x0 * x0 + y0 * y0 <= 4) {
var x1 = (x0 * x0) - (y0 * y0) + x
var y1 = 2 * x0 * y0 + y
x0 = x1
y0 = y1
iter = iter + 1
}
System.write(" .-:;+=xX$& "[iter])
}
System.print("")
}`);
} //fractal
var initial_code = new URLSearchParams(location.search).get("code")
if (initial_code !== null) {
initial_code = LZString.decompressFromEncodedURIComponent(initial_code)
set_input(initial_code)
}
} //if try_code
}

1
doc/site/static/wren.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.3 KiB

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