0.3.0; remove files no longer needed
- docs for cli will be part of wren repo for now - notes and stuff are vm side already - the rest is unused
This commit is contained in:
parent
fe186d2ea1
commit
7a6ff13290
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013 Robert Nystrom
|
||||
Copyright (c) 2013-2020 Robert Nystrom and Wren CLI 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
|
||||
|
||||
120
Makefile
120
Makefile
@ -1,120 +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.
|
||||
|
||||
# Allows one to enable verbose builds with VERBOSE=1
|
||||
V := @
|
||||
ifeq ($(VERBOSE),1)
|
||||
V :=
|
||||
endif
|
||||
|
||||
# Executables are built to bin/. Libraries are built to lib/.
|
||||
|
||||
# A normal, optimized release build for the current CPU architecture.
|
||||
# For convenience, also copies the interpreter to the top level.
|
||||
release:
|
||||
$(V) $(MAKE) -f util/wren.mk
|
||||
$(V) cp bin/wren wren
|
||||
|
||||
# A debug build for the current architecture.
|
||||
debug:
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug
|
||||
|
||||
# A release build of just the VM, both shared and static libraries.
|
||||
vm:
|
||||
$(V) $(MAKE) -f util/wren.mk vm
|
||||
|
||||
# A release build of the shared library for the VM.
|
||||
shared:
|
||||
$(V) $(MAKE) -f util/wren.mk shared
|
||||
|
||||
# A release build of the shared library for the VM.
|
||||
static:
|
||||
$(V) $(MAKE) -f util/wren.mk static
|
||||
|
||||
# Build all configurations.
|
||||
all: debug release
|
||||
$(V) $(MAKE) -f util/wren.mk LANG=cpp
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug LANG=cpp
|
||||
$(V) $(MAKE) -f util/wren.mk ARCH=32
|
||||
$(V) $(MAKE) -f util/wren.mk LANG=cpp ARCH=32
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug ARCH=32
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug LANG=cpp ARCH=32
|
||||
$(V) $(MAKE) -f util/wren.mk ARCH=64
|
||||
$(V) $(MAKE) -f util/wren.mk LANG=cpp ARCH=64
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug ARCH=64
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug LANG=cpp ARCH=64
|
||||
|
||||
# Travis uses these targets for continuous integration.
|
||||
ci: ci_32 ci_64
|
||||
|
||||
ci_32:
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug LANG=c ARCH=32 vm cli api_test
|
||||
$(V) ./util/test.py --suffix=d-32 $(suite)
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug LANG=cpp ARCH=32 vm cli api_test
|
||||
$(V) ./util/test.py --suffix=d-cpp-32 $(suite)
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=release LANG=c ARCH=32 vm cli api_test
|
||||
$(V) ./util/test.py --suffix=-32 $(suite)
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=release LANG=cpp ARCH=32 vm cli api_test
|
||||
$(V) ./util/test.py --suffix=-cpp-32 $(suite)
|
||||
|
||||
ci_64:
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug LANG=c ARCH=64 vm cli api_test
|
||||
$(V) ./util/test.py --suffix=d-64 $(suite)
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug LANG=cpp ARCH=64 vm cli api_test
|
||||
$(V) ./util/test.py --suffix=d-cpp-64 $(suite)
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=release LANG=c ARCH=64 vm cli api_test
|
||||
$(V) ./util/test.py --suffix=-64 $(suite)
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=release LANG=cpp ARCH=64 vm cli api_test
|
||||
$(V) ./util/test.py --suffix=-cpp-64 $(suite)
|
||||
|
||||
# Remove all build outputs and intermediate files. Does not remove downloaded
|
||||
# dependencies. Use cleanall for that.
|
||||
clean:
|
||||
$(V) rm -rf bin
|
||||
$(V) rm -rf build
|
||||
$(V) rm -rf lib
|
||||
|
||||
# Run the tests against the debug build of Wren.
|
||||
test: api_test debug
|
||||
$(V) ./util/test.py $(suite)
|
||||
|
||||
benchmark: release
|
||||
$(V) $(MAKE) -f util/wren.mk api_test
|
||||
$(V) ./util/benchmark.py -l wren $(suite)
|
||||
|
||||
benchmark_baseline: release
|
||||
$(V) $(MAKE) -f util/wren.mk api_test
|
||||
$(V) ./util/benchmark.py --generate-baseline
|
||||
|
||||
unit_test:
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug unit_test
|
||||
$(V) ./build/debug/test/unit_wrend
|
||||
|
||||
# Build API tests.
|
||||
api_test:
|
||||
$(V) $(MAKE) -f util/wren.mk MODE=debug api_test
|
||||
|
||||
# Generate the Wren site.
|
||||
docs:
|
||||
mkdir -p build
|
||||
$(V) ./util/generate_docs.py
|
||||
|
||||
# Continuously generate and serve the Wren site.
|
||||
servedocs:
|
||||
$(V) ./util/generate_docs.py --serve
|
||||
|
||||
# Continuously generate the Wren site.
|
||||
watchdocs:
|
||||
$(V) ./util/generate_docs.py --watch
|
||||
|
||||
# Build the docs and copy them to a local "gh-pages" directory.
|
||||
gh-pages: docs
|
||||
$(V) 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 src/optional/*.h src/vm/*.c
|
||||
mkdir -p build
|
||||
./util/generate_amalgamation.py > build/wren.c
|
||||
|
||||
.PHONY: all amalgamation api_test benchmark builtin clean debug docs gh-pages release test vm watchdocs ci ci_32 ci_64
|
||||
@ -1,78 +0,0 @@
|
||||
Q: Can we use fibers for error-handling?
|
||||
|
||||
The goal here is to avoid adding support for exception handling if we're already
|
||||
going to support fibers. A potential bonus would be being able to have
|
||||
restartable error-handling.
|
||||
|
||||
The general idea is that instead of putting code in a "try" block, you throw it
|
||||
onto a new fiber. If an error occurs, that fiber is paused, and returns control
|
||||
back to the spawning fiber. The parent fiber can then decipher the error and
|
||||
either abandon the fiber, or try to fix the error and resume somehow.
|
||||
|
||||
The first question is what kinds of errors is this useful for. For things like
|
||||
parsing strings where failure is common and error-handling needs to be
|
||||
lightweight, I think using fibers is too heavy, both in performance and code.
|
||||
A better answer there is to lean on dynamic typing and return null on parse
|
||||
failure.
|
||||
|
||||
On the other hand, it might be nice to be able to resume here if the code that
|
||||
provided the string is far away and you don't want to have to manually propagate
|
||||
the error out.
|
||||
|
||||
Programmatic errors like invalid argument types should halt the fiber but the
|
||||
programmer will not want to resume that at runtime. Using the mechanism here is
|
||||
fine since it would then dump a stack trace, etc. But it won't take advantage
|
||||
of resuming.
|
||||
|
||||
Resuming is probably useful for things like IO errors where the error can't be
|
||||
easily predicted beforehand but where you may want to handle it gracefully. For
|
||||
example, if a file can't be opened, the caller may want to wait a while and
|
||||
try again.
|
||||
|
||||
--
|
||||
|
||||
After thinking about it, maybe resuming is a bridge too far. Erlang's model is
|
||||
that a failure just kills the process. I'll note that Erlang does have try and
|
||||
catch, though.
|
||||
|
||||
The goals for error-handling in a scripting language are:
|
||||
|
||||
0. Have simple semantics and implementation.
|
||||
|
||||
1. Make it easy for developers to track down programmatic errors so they can
|
||||
fix them. This means bugs like wrong argument types should fail immediately
|
||||
and loudly, and should provide context (a callstack) about where the error
|
||||
occurred.
|
||||
|
||||
2. For runtime errors like parsing an invalid string or opening a missing file,
|
||||
the program should be able to easily detect the error at handle it.
|
||||
|
||||
3. It *may* be useful for programmers to be able to trap all errors and try to
|
||||
keep the program alive, or at least log the error in a meaningful way. When
|
||||
you have user-defined scripts, or a lot of code, or code authored by
|
||||
non-technical people, it's nice if a failure in one part can be reported but
|
||||
not take down the entire system.
|
||||
|
||||
Two close-at-hand examples:
|
||||
|
||||
- The REPL. A bug in code in the REPL shouldn't kill the whole REPL session.
|
||||
|
||||
- The test framework. In order to write tests in Wren that test programmatic
|
||||
runtime errors, we need to be able to detect them and output something.
|
||||
The test runner could just parse the error output when the entire process
|
||||
dies, but that means you can only have one error test per test file.
|
||||
|
||||
Given those, I'm thinking:
|
||||
|
||||
1. Programmatic errors take down the entire fiber and dump a callstack.
|
||||
Normally, they will also take down the parent fiber and so on until the
|
||||
entire program goes down.
|
||||
|
||||
2. Runtime errors return error codes (or null). Things like parsing a string to
|
||||
a number, etc. should just return an error that you are responsible for
|
||||
handling.
|
||||
|
||||
3. When handing off control to a fiber, there is a "guarded run" method that
|
||||
will run the fiber. If it fails with a programmatic error, the invoked fiber
|
||||
dies, but the parent does not. It gets the callstack and error as some sort
|
||||
of object it can poke at.
|
||||
@ -1,29 +0,0 @@
|
||||
Q: Can fields be implicitly declared?
|
||||
|
||||
The idea is that just using a name starting with "_" somewhere in a class
|
||||
automatically defines a field with that name. Implicit fields are particularly
|
||||
nice because it means they don't have to be defined all before methods. (Since
|
||||
we have a single-pass compiler, we would have to otherwise a method could
|
||||
only refer to previously defined fields.)
|
||||
|
||||
One potential problem is with nested classes. This is more important if we
|
||||
consider a module effectively a class. Consider:
|
||||
|
||||
class Outer {
|
||||
foo {
|
||||
_blah = "value"
|
||||
}
|
||||
|
||||
class Inner {
|
||||
IO.write(_blah) // Does this declare field in Inner, or access Outer?
|
||||
}
|
||||
}
|
||||
|
||||
Looking at this, though, I think there's already a question how referring to an
|
||||
outer field would work. Having an instance of Inner doesn't imply you also have
|
||||
an instance of Outer. We definitely don't want to recapitulate inner classes
|
||||
in Java.
|
||||
|
||||
Q: What about static fields?
|
||||
|
||||
A: Different naming convention? __foo?
|
||||
@ -1,65 +0,0 @@
|
||||
This is the number of times each instruction was executed when running the
|
||||
delta_blue benchmark:
|
||||
|
||||
3753021 CODE_LOAD_LOCAL
|
||||
2233991 CODE_RETURN
|
||||
2151580 CODE_LOAD_FIELD_THIS
|
||||
2121398 CODE_CALL_1
|
||||
1827535 CODE_CALL_0
|
||||
1328364 CODE_POP
|
||||
1136064 CODE_JUMP_IF
|
||||
715071 CODE_LOAD_GLOBAL
|
||||
428374 CODE_STORE_FIELD_THIS
|
||||
424999 CODE_NULL
|
||||
355344 CODE_STORE_LOCAL
|
||||
341762 CODE_LOOP
|
||||
118855 CODE_CONSTANT
|
||||
93048 CODE_CALL_2
|
||||
75280 CODE_AND
|
||||
59920 CODE_JUMP
|
||||
16842 CODE_LIST
|
||||
16660 CODE_TRUE
|
||||
10040 CODE_OR
|
||||
8200 CODE_LOAD_UPVALUE
|
||||
8140 CODE_SUPER_1
|
||||
6540 CODE_FALSE
|
||||
6076 CODE_STORE_GLOBAL
|
||||
4000 CODE_SUPER_3
|
||||
2020 CODE_SUPER_2
|
||||
2000 CODE_SUPER_0
|
||||
2000 CODE_CALL_5
|
||||
2000 CODE_CALL_3
|
||||
160 CODE_CLOSURE
|
||||
74 CODE_METHOD_INSTANCE
|
||||
11 CODE_CLASS
|
||||
4 CODE_METHOD_STATIC
|
||||
0 CODE_SUPER_9
|
||||
0 CODE_SUPER_8
|
||||
0 CODE_SUPER_7
|
||||
0 CODE_SUPER_6
|
||||
0 CODE_SUPER_5
|
||||
0 CODE_SUPER_4
|
||||
0 CODE_SUPER_16
|
||||
0 CODE_SUPER_15
|
||||
0 CODE_SUPER_14
|
||||
0 CODE_SUPER_13
|
||||
0 CODE_SUPER_12
|
||||
0 CODE_SUPER_11
|
||||
0 CODE_SUPER_10
|
||||
0 CODE_STORE_UPVALUE
|
||||
0 CODE_STORE_FIELD
|
||||
0 CODE_LOAD_FIELD
|
||||
0 CODE_IS
|
||||
0 CODE_CLOSE_UPVALUE
|
||||
0 CODE_CALL_9
|
||||
0 CODE_CALL_8
|
||||
0 CODE_CALL_7
|
||||
0 CODE_CALL_6
|
||||
0 CODE_CALL_4
|
||||
0 CODE_CALL_16
|
||||
0 CODE_CALL_15
|
||||
0 CODE_CALL_14
|
||||
0 CODE_CALL_13
|
||||
0 CODE_CALL_12
|
||||
0 CODE_CALL_11
|
||||
0 CODE_CALL_10
|
||||
@ -1,261 +0,0 @@
|
||||
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`).
|
||||
@ -1,144 +0,0 @@
|
||||
## 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.
|
||||
@ -1,146 +0,0 @@
|
||||
var baz = "top level"
|
||||
|
||||
class Foo {
|
||||
bar {
|
||||
baz
|
||||
_baz
|
||||
}
|
||||
|
||||
baz { "getter" }
|
||||
_baz { "private getter" }
|
||||
|
||||
this {
|
||||
_baz = "field"
|
||||
}
|
||||
}
|
||||
|
||||
Given `_foo`, how do we tell if it is:
|
||||
1. A call to a private getter
|
||||
2. Accessing a private field
|
||||
3. Tearing off a reference to a private method
|
||||
|
||||
It's not 3 because of arity overloading. Wren doesn't really have method
|
||||
tear-off because of this.)
|
||||
|
||||
This is hard because the getter may not be defined yet. One option is:
|
||||
It's always a call to a private getter. After the class is defined, we see if
|
||||
there are any private getters that were not implemented and define implicit
|
||||
getters for them that return fields.
|
||||
|
||||
That's weird if you take into account setters, though. Consider:
|
||||
|
||||
class Foo {
|
||||
a { IO.write(_prop) }
|
||||
_prop = value { ... }
|
||||
}
|
||||
|
||||
For first reference to `_prop`, compile it to getter call. Then see setter
|
||||
defined for it, so we no longer implicitly make a field. But there's no getter,
|
||||
so now the above call will fail.
|
||||
|
||||
Probably do want call to fail here, so that may be OK.
|
||||
|
||||
---
|
||||
|
||||
Given `_foo(arg)`, how do we tell if it is:
|
||||
|
||||
1. A call to a private method
|
||||
2. A call to a private getter, which returns a field that's a fn, and invoking
|
||||
it.
|
||||
|
||||
Since arity is part of the name, the answer here is 1.
|
||||
|
||||
---
|
||||
|
||||
Given `foo(arg)` inside a class, how do we tell if it is:
|
||||
|
||||
1. A call to a method on this.
|
||||
2. Accessing a field `foo` on this, which returns a fn, and invoking it.
|
||||
3. Calling a getter `foo` on this, which returns a fn, and invoking it.
|
||||
4. A call to a top-level fn.
|
||||
|
||||
Let's just dismiss 3. Since arity affects naming, `foo(arg)` and `(foo)(arg)`
|
||||
are really different things in Wren. The parentheses and args are effectively
|
||||
part of the name.
|
||||
|
||||
That covers 2 as well. If we ditch top level fns, we're left with 1. This is
|
||||
good, I think. It means the common case of calling methods on yourself is nice
|
||||
and terse.
|
||||
|
||||
---
|
||||
|
||||
Given `foo` inside a class, how do we tell if it is:
|
||||
|
||||
1. Accessing a field on this.
|
||||
2. Calling a getter on this.
|
||||
3. Accessing a global variable.
|
||||
4. Accessing a top-level getter.
|
||||
5. Accessing a local variable.
|
||||
|
||||
We can probably ditch 4. We can ditch 1 because Wren doesn't have public fields.
|
||||
|
||||
Because both getters and global variables can be used before they are defined,
|
||||
we can't determine statically (in a single pass compiler) if there is a global
|
||||
variable or getter named `foo` in order to disambiguate. Even if we could, we'd
|
||||
still have to answer the ambiguous case where it's both.
|
||||
|
||||
If we assume it's a global and the user wants a getter, they can always do
|
||||
`this.foo` to be explicit. If we assume it's getter, how would they indicate a
|
||||
global?
|
||||
|
||||
One option is to have a different naming convention for globals, like a
|
||||
capitalized initial variable. That lines up with class names at the top level
|
||||
anyway. It just means if we have variables for imported modules, we'll want to
|
||||
capitalize those.
|
||||
|
||||
We still have to distinguish locals, but since those are declared before use, we
|
||||
can determine that statically. I.e. locals will shadow implicit getters.
|
||||
|
||||
---
|
||||
|
||||
OK, so here's one proposal:
|
||||
|
||||
class MyClass {
|
||||
method {
|
||||
_foo // access field
|
||||
_foo(arg) // not valid
|
||||
foo // local var or getter on this
|
||||
foo(arg) // method on this
|
||||
Foo // global variable
|
||||
}
|
||||
}
|
||||
|
||||
This is simple, and straightforward to compile. Using capitalization for globals
|
||||
is a bit weird, and not having private methods is a bummer, but maybe simplicity
|
||||
is the right answer.
|
||||
|
||||
Here's another:
|
||||
|
||||
class MyClass {
|
||||
method {
|
||||
_foo // private getter on this
|
||||
_foo(arg) // private method on this
|
||||
foo // local var or getter on this
|
||||
foo(arg) // method on this
|
||||
Foo // global variable
|
||||
}
|
||||
}
|
||||
|
||||
To get rid of the weird capitalization rule for globals, one option is to not
|
||||
allow forward references to globals. That would break mutually recursive
|
||||
references to classes, though:
|
||||
|
||||
class A {
|
||||
foo { B.new }
|
||||
}
|
||||
|
||||
class B {
|
||||
foo { A.new }
|
||||
}
|
||||
|
||||
So, not a fan of that.
|
||||
|
||||
Ignoring that, the main difference between the two proposals is the second has
|
||||
private methods. Since the first proposal is practically a subset of the second,
|
||||
let's start with that one first.
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
Q1: What does this resolve to:
|
||||
|
||||
foo(arg)
|
||||
|
||||
It could be:
|
||||
1. this.foo(arg)
|
||||
2. EnclosingClass.foo(arg) // i.e. a static method call
|
||||
3. a call to a top-level function foo()
|
||||
|
||||
If we adopt the idea that a module is just a class definition (with some
|
||||
syntactic differences) and classes can be nested, then 3 really means "a call
|
||||
to a static method on the class surrounding the enclosing class".
|
||||
|
||||
I *don't* think we want the answer to the question to vary based on the name
|
||||
in question. We can't rely on name resolution to disambiguate because we don't
|
||||
know the full set of surrounding names in a single pass compiler. Also, it's
|
||||
semantically squishier.
|
||||
|
||||
I think the right answer is 1, it's an implicit call on this. That's what you
|
||||
want most often, I think. For imported modules, we could import them with a
|
||||
"prefix" (really import them as objects bound to named variables), so calling
|
||||
a top-level function in another module would be something like:
|
||||
|
||||
someModule.foo(arg)
|
||||
|
||||
This leaves the question of how *do* you call top level functions in your own
|
||||
module? I.e., how do we call foo here:
|
||||
|
||||
def foo(arg) { IO.write("called foo!") }
|
||||
|
||||
class SomeClass {
|
||||
bar {
|
||||
// Want to call foo here...
|
||||
}
|
||||
}
|
||||
|
||||
This is analogous to:
|
||||
|
||||
class SomeModule {
|
||||
static foo(arg) { IO.write("called foo!") }
|
||||
|
||||
class SomeClass {
|
||||
bar {
|
||||
// Want to call foo here...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The obvious solution is to use the class name:
|
||||
|
||||
class SomeModule {
|
||||
static foo(arg) { IO.print("called foo!") }
|
||||
|
||||
class SomeClass {
|
||||
bar {
|
||||
SomeModule.foo(arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Which just leaves the question of what the class name of a top-level "module
|
||||
class" is.
|
||||
|
||||
Idea: it's unnamed, so you just use a leading ".":
|
||||
|
||||
def foo(arg) { IO.print("called foo!") }
|
||||
|
||||
class SomeClass {
|
||||
bar {
|
||||
.foo(arg)
|
||||
}
|
||||
}
|
||||
|
||||
This mirrors C++'s unnamed scope thing:
|
||||
|
||||
::foo(arg);
|
||||
@ -1,478 +0,0 @@
|
||||
# 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 master 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.
|
||||
@ -1,90 +0,0 @@
|
||||
^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](http://wren.io/blog/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)
|
||||
- Visit the [wren-lang organization](https://github.com/wren-lang) on Github to get involved.
|
||||
- Follow the developers [@munificentbob](https://twitter.com/munificentbob) or [@___discovery](https://twitter.com/___discovery) on twitter
|
||||
|
||||
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
^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 (underscorediscovery)](https://github.com/underscorediscovery) 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 master (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)
|
||||
- Visit the [wren-lang organization](https://github.com/wren-lang) on Github.
|
||||
- Follow [@munificentbob](https://twitter.com/munificentbob) or [@___discovery](https://twitter.com/___discovery) on twitter
|
||||
|
||||
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
^title Development blogs
|
||||
|
||||
[<h3>0.2.0 and beyond</h3>](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>](hello-wren.html)
|
||||
> <date>4 Feb 2019</date> • A short post introducing the blog, the new maintainer, and the immediate term plans for Wren.
|
||||
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
<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.2.0 and beyond</title>
|
||||
<link>https://wren.io/blog/0.2.0-and-beyond.html</link>
|
||||
<description>Checkpoints, and the plans for 0.3.0.</description>
|
||||
<guid>https://wren.io/blog/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/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/hello-wren.html</guid>
|
||||
<pubDate>Mon, 04 Feb 2019 00:00:00 GMT</pubDate>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
@ -1,56 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="getting-started.html">Getting Started</a></td>
|
||||
<td><a href="contributing.html">Contributing</a></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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,592 +0,0 @@
|
||||
^title Classes
|
||||
|
||||
Every value in Wren is an object, and every object is an instance of a class.
|
||||
Even `true` and `false` are full-featured objects—instances of the
|
||||
[Bool][] class.
|
||||
|
||||
[bool]: modules/core/bool.html
|
||||
|
||||
Classes define an objects *behavior* and *state*. Behavior is defined by
|
||||
[*methods*][method calls] which live in the class. Every object of the same
|
||||
class supports the same methods. State is defined in *fields*, whose values are
|
||||
stored in each instance.
|
||||
|
||||
[method calls]: method-calls.html
|
||||
|
||||
## Defining a class
|
||||
|
||||
Classes are created using the `class` keyword, unsurprisingly:
|
||||
|
||||
:::wren
|
||||
class Unicorn {}
|
||||
|
||||
This creates a class named `Unicorn` with no methods or fields.
|
||||
|
||||
## Methods
|
||||
|
||||
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!")
|
||||
}
|
||||
}
|
||||
|
||||
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).")
|
||||
}
|
||||
}
|
||||
|
||||
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!")
|
||||
}
|
||||
|
||||
prance(where) {
|
||||
System.print("The unicorn prances in %(where).")
|
||||
}
|
||||
|
||||
prance(where, when) {
|
||||
System.print("The unicorn prances in %(where) at %(when).")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
operation and have to check for missing optional arguments. In Wren, they are
|
||||
different methods that you implement separately.
|
||||
|
||||
In addition to named methods with parameter lists, Wren has a bunch of other
|
||||
different syntaxes for methods. Your classes can define all of them.
|
||||
|
||||
### Getters
|
||||
|
||||
A getter leaves off the parameter list and the parentheses:
|
||||
|
||||
:::wren
|
||||
class Unicorn {
|
||||
// Unicorns are always fancy.
|
||||
isFancy { true }
|
||||
}
|
||||
|
||||
### 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).")
|
||||
}
|
||||
}
|
||||
|
||||
By convention, the parameter is usually named `value` but you can call it
|
||||
whatever makes your heart flutter.
|
||||
|
||||
### Operators
|
||||
|
||||
Prefix operators, like getters, have no parameter list:
|
||||
|
||||
:::wren
|
||||
class Unicorn {
|
||||
- {
|
||||
System.print("Negating a unicorn is weird.")
|
||||
}
|
||||
}
|
||||
|
||||
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.")
|
||||
}
|
||||
}
|
||||
|
||||
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!")
|
||||
}
|
||||
|
||||
[x, y] {
|
||||
System.print("Unicorns are not matrices either!")
|
||||
}
|
||||
}
|
||||
|
||||
Unlike with named methods, you can't define a subscript operator with an empty
|
||||
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)!")
|
||||
}
|
||||
}
|
||||
|
||||
## Method Scope
|
||||
|
||||
Up to this point, "[scope][]" has been used to talk exclusively about
|
||||
[variables][]. In a procedural language like C, or a functional one like Scheme,
|
||||
that's the only kind of scope there is. But object-oriented languages like Wren
|
||||
introduce another kind of scope: *object scope*. It contains the methods that
|
||||
are available on an object. When you write:
|
||||
|
||||
[scope]: variables.html#scope
|
||||
[variables]: variables.html
|
||||
|
||||
:::wren
|
||||
unicorn.isFancy
|
||||
|
||||
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.
|
||||
|
||||
### `this`
|
||||
|
||||
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" }
|
||||
|
||||
printName() {
|
||||
System.print(this.name) //> Francis
|
||||
}
|
||||
}
|
||||
|
||||
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][] declared *inside* a method. When you do,
|
||||
`this` still refers to the instance whose *method* is being called:
|
||||
|
||||
:::wren
|
||||
class Unicorn {
|
||||
name { "Francis" }
|
||||
|
||||
printNameThrice() {
|
||||
(1..3).each {
|
||||
// Use "this" inside the function passed to each().
|
||||
System.print(this.name) //> Francis
|
||||
} //> Francis
|
||||
} //> Francis
|
||||
}
|
||||
|
||||
[function]: functions.html
|
||||
|
||||
This is unlike Lua and JavaScript which can "forget" `this` when you create a
|
||||
callback inside a method. Wren does what you want here and retains the
|
||||
reference to the original object.
|
||||
|
||||
(In technical terms, a function's closure includes `this`. Wren can do this
|
||||
because it makes a distinction between methods and functions.)
|
||||
|
||||
### Implicit `this`
|
||||
|
||||
Using `this.` every time you want to call a method on yourself works, but it's
|
||||
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" }
|
||||
|
||||
printName() {
|
||||
System.print(name) //> Francis
|
||||
}
|
||||
}
|
||||
|
||||
Code like this gets tricky when there is also a variable outside of the class
|
||||
with the same name. Consider:
|
||||
|
||||
:::wren
|
||||
var name = "variable"
|
||||
|
||||
class Unicorn {
|
||||
name { "Francis" }
|
||||
|
||||
printName() {
|
||||
System.print(name) // ???
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
the program, but it also has the object scope of the methods on `this`.
|
||||
|
||||
Which scope wins? Every language has to decide how to handle this and there
|
||||
is a surprising plethora of approaches. Wren's approach to resolving a name
|
||||
inside a method works like this:
|
||||
|
||||
1. If there is a local variable inside the method with that name, that wins.
|
||||
2. Else, if the name starts with a lowercase letter, treat it like a method on
|
||||
`this`.
|
||||
3. Otherwise, look for a variable with that name in the surrounding scope.
|
||||
|
||||
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
|
||||
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
|
||||
of the class, it's usually the name of some other class. This rule makes that
|
||||
work.
|
||||
|
||||
Here's an example that shows all three cases:
|
||||
|
||||
:::wren
|
||||
var shadowed = "surrounding"
|
||||
var lowercase = "surrounding"
|
||||
var Capitalized = "surrounding"
|
||||
|
||||
class Scope {
|
||||
shadowed { "object" }
|
||||
lowercase { "object" }
|
||||
Capitalized { "object" }
|
||||
|
||||
test() {
|
||||
var shadowed = "local"
|
||||
|
||||
System.print(shadowed) //> local
|
||||
System.print(lowercase) //> object
|
||||
System.print(Capitalized) //> surrounding
|
||||
}
|
||||
}
|
||||
|
||||
It's a bit of a strange rule, but Ruby works more or less the same way.
|
||||
|
||||
## Constructors
|
||||
|
||||
We've seen how to define kinds of objects and how to declare methods on them.
|
||||
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 + ".")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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")
|
||||
|
||||
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.")
|
||||
}
|
||||
}
|
||||
|
||||
var dave = Unicorn.brown("Dave")
|
||||
|
||||
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
|
||||
classes aren't designed to be constructed. If you have an abstract base class
|
||||
that just contains methods to be inherited by other classes, it doesn't need
|
||||
and won't have a constructor.
|
||||
|
||||
Like other methods, constructors can obviously have arguments, and can be
|
||||
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 is actually a pair of methods. You get a method on the class:
|
||||
|
||||
:::wren
|
||||
Unicorn.brown("Dave")
|
||||
|
||||
That creates the new instance, then it invokes the *initializer* on that
|
||||
instance. This is where the constructor body you defined gets run.
|
||||
|
||||
This distinction is important because it means inside the body of the
|
||||
constructor, you can access `this`, assign [fields](#fields), call superclass
|
||||
constructors, etc.
|
||||
|
||||
## Fields
|
||||
|
||||
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 }
|
||||
|
||||
// Other stuff...
|
||||
}
|
||||
|
||||
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`
|
||||
and `this.height` in other languages.
|
||||
|
||||
When a field name appears, Wren looks for the nearest enclosing class and looks
|
||||
up the field on the instance of that class. Field names cannot be used outside
|
||||
of an instance method. They *can* be used inside a [function](functions.html)
|
||||
in a method. Wren will look outside any nested functions until it finds an
|
||||
enclosing method.
|
||||
|
||||
Unlike [variables](variables.html), fields are implicitly declared by simply
|
||||
assigning to them. If you access a field before it has been initialized, its
|
||||
value is `null`.
|
||||
|
||||
### Encapsulation
|
||||
|
||||
All fields are *private* in Wren—an object's fields can only be directly
|
||||
accessed from within methods defined on the object's class.
|
||||
|
||||
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 }
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
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!
|
||||
}
|
||||
}
|
||||
...
|
||||
|
||||
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
|
||||
keeping your object's state pretty tightly bundled up. Don't feel that you have
|
||||
to or even should define getters or setters for most of your object's fields.
|
||||
|
||||
## Metaclasses and static members
|
||||
|
||||
**TODO**
|
||||
|
||||
### Static fields
|
||||
|
||||
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() {}
|
||||
|
||||
static setFromStatic(a) { __a = a }
|
||||
setFromInstance(a) { __a = a }
|
||||
|
||||
static printFromStatic() {
|
||||
System.print(__a)
|
||||
}
|
||||
|
||||
printFromInstance() {
|
||||
System.print(__a)
|
||||
}
|
||||
}
|
||||
|
||||
Just like instance fields, static fields are initially `null`:
|
||||
|
||||
:::wren
|
||||
Foo.printFromStatic() //> null
|
||||
|
||||
They can be used from static methods:
|
||||
|
||||
:::wren
|
||||
Foo.setFromStatic("first")
|
||||
Foo.printFromStatic() //> first
|
||||
|
||||
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()
|
||||
|
||||
foo1.setFromInstance("second")
|
||||
foo2.printFromInstance() //> second
|
||||
|
||||
## Inheritance
|
||||
|
||||
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
|
||||
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 {}
|
||||
|
||||
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
|
||||
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 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 }
|
||||
}
|
||||
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
Pegasus.canFly //! Static methods are not inherited.
|
||||
|
||||
This also means constructors are not inherited:
|
||||
|
||||
:::wren
|
||||
class Unicorn {
|
||||
construct new(name) {
|
||||
System.print("My name is " + name + ".")
|
||||
}
|
||||
}
|
||||
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
Pegasus.new("Fred") //! Pegasus does not define new().
|
||||
|
||||
Each class gets to control how it may be constructed independently of its base
|
||||
classes. However, constructor *initializers* are inherited since those are
|
||||
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 + ".")
|
||||
}
|
||||
}
|
||||
|
||||
class Pegasus is Unicorn {
|
||||
construct new(name) {
|
||||
super(name)
|
||||
}
|
||||
}
|
||||
|
||||
Pegasus.new("Fred") //> My name is Fred
|
||||
|
||||
## Super
|
||||
|
||||
**TODO: Integrate better into page. Should explain this before mentioning
|
||||
super above.**
|
||||
|
||||
Sometimes you want to invoke a method on yourself, but using methods defined in
|
||||
one of your [superclasses](classes.html#inheritance). You typically do this in
|
||||
an overridden method when you want to access the original method being
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
class Derived is Base {
|
||||
method() {
|
||||
super.method() //> base method
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
class Derived is Base {
|
||||
construct new() {
|
||||
super("value") //> base got value
|
||||
}
|
||||
}
|
||||
|
||||
<br><hr>
|
||||
<a class="right" href="concurrency.html">Concurrency →</a>
|
||||
<a href="functions.html">← Functions</a>
|
||||
@ -1,186 +0,0 @@
|
||||
^title Concurrency
|
||||
|
||||
Lightweight concurrency is a key feature of Wren and it is expressed using
|
||||
*fibers*. They control how all code is executed, and take the place of
|
||||
exceptions in [error handling](error-handling.html).
|
||||
|
||||
Fibers are a bit like threads except they are *cooperatively* scheduled. That
|
||||
means Wren doesn't pause one fiber and switch to another until you tell it to.
|
||||
You don't have to worry about context switches at random times and all of the
|
||||
headaches those cause.
|
||||
|
||||
Wren takes care of all of the fibers in the VM, so they don't use OS thread
|
||||
resources, or require heavyweight context switches. Each just needs a bit of
|
||||
memory for its stack. A fiber will get garbage collected like any other object
|
||||
when not referenced any more, so you can create them freely.
|
||||
|
||||
They are lightweight enough that you can, for example, have a separate fiber for
|
||||
each entity in a game. Wren can handle thousands of them without breaking a
|
||||
sweat. For example, when you run Wren in interactive mode, it creates a new
|
||||
fiber for every line of code you type in.
|
||||
|
||||
## Creating fibers
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {
|
||||
System.print("This runs in a separate fiber.")
|
||||
}
|
||||
|
||||
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 run it by calling its `call()` method:
|
||||
|
||||
:::wren
|
||||
fiber.call()
|
||||
|
||||
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("It's alive!")
|
||||
}
|
||||
|
||||
System.print(fiber.isDone) //> false
|
||||
fiber.call() //> It's alive!
|
||||
System.print(fiber.isDone) //> true
|
||||
|
||||
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
|
||||
|
||||
The main difference between fibers and functions is that a fiber can be
|
||||
suspended in the middle of its operation and then resumed later. Calling
|
||||
another fiber is one way to suspend a fiber, but that's more or less the same
|
||||
as one function calling another.
|
||||
|
||||
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 make a fiber yield by calling the static `yield()` method on Fiber:
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {
|
||||
System.print("Before yield")
|
||||
Fiber.yield()
|
||||
System.print("Resumed")
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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 you create a fiber using a function that takes a parameter, you can pass a
|
||||
value to it through `call()`:
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {|param|
|
||||
System.print(param)
|
||||
}
|
||||
|
||||
fiber.call("Here you go") //> Here you go
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {|param|
|
||||
System.print(param)
|
||||
var result = Fiber.yield()
|
||||
System.print(result)
|
||||
}
|
||||
|
||||
fiber.call("First") //> First
|
||||
fiber.call("Second") //> Second
|
||||
|
||||
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("Reply")
|
||||
}
|
||||
|
||||
System.print(fiber.call()) //> Reply
|
||||
|
||||
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
|
||||
|
||||
What we've seen so far is very similar to what you can do with languages like
|
||||
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*—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 — the entire callstack — gets suspended. For example:
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {
|
||||
(1..10).each {|i|
|
||||
Fiber.yield(i)
|
||||
}
|
||||
}
|
||||
|
||||
Here, we're calling `yield()` from within a [function](functions.html) being
|
||||
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
|
||||
|
||||
Fibers have one more trick up their sleeves. When you execute a fiber using
|
||||
`call()`, the fiber tracks which fiber it will return to when it yields. This
|
||||
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 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 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 →</a>
|
||||
<a href="classes.html">← Classes</a>
|
||||
@ -1,142 +0,0 @@
|
||||
^title Contributing
|
||||
|
||||
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,
|
||||
"Hi". There are no strangers to Wren, just friends we haven't met yet.
|
||||
|
||||
## Growing the ecosystem
|
||||
|
||||
The simplest and often most helpful way to join the Wren party is to be a Wren
|
||||
*user*. Create an application that embeds Wren. Write a library or a handy
|
||||
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:
|
||||
|
||||
[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
|
||||
languages.
|
||||
* Other [tools and utilities][] that make it easier to be a Wren programmer.
|
||||
|
||||
[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
|
||||
|
||||
You're also more than welcome to contribute to Wren itself, both the core VM and
|
||||
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.
|
||||
|
||||
### Finding something to hack on
|
||||
|
||||
Between the [issue tracker][issue] and searching for `TODO` comments in the
|
||||
code, it's pretty easy to find something that needs doing, though we don't
|
||||
always do a good job of writing everything down.
|
||||
|
||||
If nothing there suits your fancy, new ideas are welcome as well! If you have an
|
||||
idea for a significant change or addition, please file a [proposal][] to discuss
|
||||
it before writing lots of code. Wren tries very *very* hard to be minimal which
|
||||
means often having to say "no" to language additions, even really cool ones.
|
||||
|
||||
### Hacking on docs
|
||||
|
||||
The [documentation][] is one of the easiest—and most
|
||||
important!—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.
|
||||
|
||||
[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 python3 setup.py develop
|
||||
$ cd ../.. # Back to the root Wren directory.
|
||||
|
||||
Now you can build the docs:
|
||||
|
||||
:::sh
|
||||
$ make docs
|
||||
|
||||
This generates the site in `build/docs/`. You can run any simple static web
|
||||
server from there. Python includes one:
|
||||
|
||||
:::sh
|
||||
$ cd build/docs
|
||||
$ python3 -m http.server
|
||||
|
||||
Running `make docs` is a drag every time you change a line of Markdown or SASS,
|
||||
so there is also a file watching version that will automatically regenerate the
|
||||
docs when you edit a file:
|
||||
|
||||
:::sh
|
||||
$ make watchdocs
|
||||
|
||||
### Hacking on the VM
|
||||
|
||||
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:
|
||||
|
||||
:::sh
|
||||
$ make test
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
expectations.
|
||||
|
||||
5. **Make sure the tests all pass, both the old ones and your new ones.**
|
||||
|
||||
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.
|
||||
|
||||
## 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
|
||||
[/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/wren-lang/wren
|
||||
[fork]: https://help.github.com/articles/fork-a-repo/
|
||||
[docs]: https://github.com/wren-lang/wren/tree/master/doc/site
|
||||
[issue]: https://github.com/wren-lang/wren/issues
|
||||
[proposal]: https://github.com/wren-lang/wren/labels/proposal
|
||||
[feature branches]: https://www.atlassian.com/git/tutorials/comparing-workflows/centralized-workflow
|
||||
[authors]: https://github.com/wren-lang/wren/tree/master/AUTHORS
|
||||
[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/
|
||||
@ -1,245 +0,0 @@
|
||||
^title Control Flow
|
||||
|
||||
Control flow is used to determine which chunks of code are executed and how many
|
||||
times. *Branching* statements and expressions decide whether or not to execute
|
||||
some code and *looping* ones execute something more than once.
|
||||
|
||||
## Truth
|
||||
|
||||
All control flow is based on *deciding* whether or not to do something. This
|
||||
decision depends on some expression's value. We take the entire universe of
|
||||
possible objects and divide them into two buckets: some we consider "true" and
|
||||
the rest are "false". If the expression results in a value in the true bucket,
|
||||
we do one thing. Otherwise, we do something else.
|
||||
|
||||
Obviously, the boolean `true` is in the "true" bucket and `false` is in
|
||||
"false", but what about values of other types? The choice is ultimately
|
||||
arbitrary, and different languages have different rules. Wren's rules follow
|
||||
Ruby:
|
||||
|
||||
* The boolean value `false` is false.
|
||||
* The null value `null` is false.
|
||||
* Everything else is true.
|
||||
|
||||
This means `0`, empty strings, and empty collections are all considered "true"
|
||||
values.
|
||||
|
||||
## If statements
|
||||
|
||||
The simplest branching statement, `if` lets you conditionally skip a chunk of
|
||||
code. It looks like this:
|
||||
|
||||
:::wren
|
||||
if (ready) System.print("go!")
|
||||
|
||||
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!")
|
||||
}
|
||||
|
||||
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!")
|
||||
|
||||
And, of course, it can take a block too:
|
||||
|
||||
:::wren
|
||||
if (ready) {
|
||||
System.print("go!")
|
||||
} else {
|
||||
System.print("not ready!")
|
||||
}
|
||||
|
||||
## 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—they short-circuit.
|
||||
|
||||
[operators]: method-calls.html#operators
|
||||
[method calls]: method-calls.html
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
## 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.
|
||||
|
||||
:::wren
|
||||
System.print(1 != 2 ? "math is sane" : "math is not sane!")
|
||||
|
||||
It takes a condition expression, followed by `?`, followed by a then
|
||||
expression, a `:`, then an else expression. Just like `if`, it evaluates the
|
||||
condition. If true, it evaluates and returns the then expression. Otherwise
|
||||
it does the else expression.
|
||||
|
||||
## While statements
|
||||
|
||||
It's hard to write a useful program without executing some chunk of code
|
||||
repeatedly. To do that, you use looping statements. There are two in Wren, and
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
condition again. It keeps doing this as long as the condition evaluates to
|
||||
something true.
|
||||
|
||||
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
|
||||
|
||||
## For statements
|
||||
|
||||
While statements are useful when you want to loop indefinitely or according to
|
||||
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)
|
||||
}
|
||||
|
||||
A `for` loop has three components:
|
||||
|
||||
1. A *variable name* to bind. In the example, that's `beatle`. Wren will create
|
||||
a new variable with that name whose scope is the body of the loop.
|
||||
|
||||
2. A *sequence expression*. This determines what you're looping over. It gets
|
||||
evaluated *once* before the body of the loop. In this case, it's a list
|
||||
literal, but it can be any expression.
|
||||
|
||||
3. A *body*. This is a curly block or a single statement. It gets executed once
|
||||
for each iteration of the loop.
|
||||
|
||||
## Break statements
|
||||
|
||||
Sometimes, right in the middle of a loop body, you decide you want to bail out
|
||||
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
|
||||
|
||||
## Numeric ranges
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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.
|
||||
Like [other operators][operators], they are special syntax for a regular method
|
||||
call. The number type implements them and returns a [range object][] that knows
|
||||
how to iterate over a series of numbers.
|
||||
|
||||
[range object]: values.html#ranges
|
||||
|
||||
## The iterator protocol
|
||||
|
||||
Lists and ranges cover the two most common kinds of loops, but you should also
|
||||
be able to define your own sequences. To enable that, the semantics of `for`
|
||||
are defined in terms of an "iterator protocol". The loop itself doesn't know
|
||||
anything about lists or ranges, it just knows how to call two particular
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
you can use). It also creates a hidden "iterator" variable and initializes it
|
||||
to `null`.
|
||||
|
||||
Each iteration, it calls `iterate()` on the sequence, passing in the current
|
||||
iterator value. (In the first iteration, it passes in `null`.) The sequence's
|
||||
job is to take that iterator and advance it to the next element in the
|
||||
sequence. (Or, in the case where the iterator is `null`, to advance it to the
|
||||
*first* element). It then returns either the new iterator, or `false` to
|
||||
indicate that there are no more elements.
|
||||
|
||||
If `false` is returned, Wren exits out of the loop and we're done. If anything
|
||||
else is returned, that means that we have advanced to a new valid element. To
|
||||
get that, Wren then calls `iteratorValue()` on the sequence and passes in the
|
||||
iterator value that it just got from calling `iterate()`. The sequence uses
|
||||
that to look up and return the appropriate element.
|
||||
|
||||
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 →</a>
|
||||
<a href="method-calls.html">← Method Calls</a>
|
||||
@ -1,136 +0,0 @@
|
||||
^title Calling C from Wren
|
||||
|
||||
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:
|
||||
|
||||
* We want to execute code written in C.
|
||||
* We want to store raw C data.
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
class Math {
|
||||
foreign static add(a, b)
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
WrenForeignMethodFn bindForeignMethodFn(
|
||||
WrenVM* vm,
|
||||
const char* module,
|
||||
const char* className,
|
||||
bool isStatic,
|
||||
const char* signature);
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
bindForeignMethodFn(vm, "main", "Math", true, "add(_,_)");
|
||||
|
||||
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:
|
||||
|
||||
:::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...
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
void foreignMethod(WrenVM* vm);
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
void mathAdd(WrenVM* vm)
|
||||
{
|
||||
double a = wrenGetSlotDouble(vm, 1);
|
||||
double b = wrenGetSlotDouble(vm, 2);
|
||||
wrenSetSlotDouble(vm, 0, a + b);
|
||||
}
|
||||
|
||||
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 →</a>
|
||||
<a href="calling-wren-from-c.html">← Calling Wren from C</a>
|
||||
@ -1,183 +0,0 @@
|
||||
^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.
|
||||
|
||||
It's also not an effective way to communicate. You can't pass arguments to
|
||||
Wren—at least, not without doing something nasty like converting them to
|
||||
literals in a string of source code—and you can't get a result value back.
|
||||
|
||||
`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, 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][].
|
||||
|
||||
[signature]: ../method-calls.html#signature
|
||||
|
||||
* **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
|
||||
|
||||
When you run a chunk of Wren code like this:
|
||||
|
||||
:::wren
|
||||
object.someMethod(1, 2, 3)
|
||||
|
||||
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—at the very least hashing the signature—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.
|
||||
|
||||
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.
|
||||
|
||||
[perf]: ../performance.html
|
||||
|
||||
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);
|
||||
|
||||
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 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
|
||||
|
||||
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.
|
||||
|
||||
[last section]: slots-and-handles.html
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
class GameEngine {
|
||||
static update(elapsedTime) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
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]. We can get a handle to the above class like so:
|
||||
|
||||
[variable]: slots-and-handles.html#looking-up-variables
|
||||
|
||||
:::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:
|
||||
|
||||
:::c
|
||||
// Load the class into slot 0.
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "main", "GameEngine", 0);
|
||||
WrenHandle* gameEngineClass = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
Now, each time we want to call a method on GameEngine, we store that value back
|
||||
in slot zero:
|
||||
|
||||
:::c
|
||||
wrenSetSlotHandle(vm, 0, gameEngineClass);
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
wrenSetSlotDouble(vm, 1, elapsedTime);
|
||||
|
||||
## 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:
|
||||
|
||||
:::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][].
|
||||
|
||||
[suspends]: ../modules/core/fiber.html#fiber.suspend()
|
||||
|
||||
`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.)
|
||||
|
||||
## 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...
|
||||
|
||||
<a class="right" href="calling-c-from-wren.html">Calling C From Wren →</a>
|
||||
<a href="slots-and-handles.html">← Slots and Handles</a>
|
||||
@ -1,200 +0,0 @@
|
||||
^title Configuring the VM
|
||||
|
||||
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.
|
||||
|
||||
The struct looks like:
|
||||
|
||||
:::c
|
||||
typedef struct
|
||||
{
|
||||
WrenReallocateFn reallocateFn;
|
||||
WrenLoadModuleFn loadModuleFn;
|
||||
WrenBindForeignMethodFn bindForeignMethodFn;
|
||||
WrenBindForeignClassFn bindForeignClassFn;
|
||||
WrenWriteFn writeFn;
|
||||
WrenErrorFn errorFn;
|
||||
size_t initialHeapSize;
|
||||
size_t minHeapSize;
|
||||
int heapGrowthPercent;
|
||||
} WrenConfiguration;
|
||||
|
||||
Most fields have useful defaults, which you can (and should) initialize by
|
||||
calling:
|
||||
|
||||
:::c
|
||||
wrenInitConfiguration(&configuration);
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
char* loadModule(WrenVM* vm, const char* name)
|
||||
|
||||
When a module is imported, Wren calls this and passes in the module's name. The
|
||||
host should return the source code for that module. Memory for the source should
|
||||
be allocated using the same allocator that the VM uses for other allocation (see
|
||||
below). Wren will take ownership of the returned string and free it later.
|
||||
|
||||
The module loader is only be called once for any given module name. Wren caches
|
||||
the result internally so subsequent imports of the same module use the
|
||||
previously loaded code.
|
||||
|
||||
If your host application isn't able to load a module with some name, it should
|
||||
return `NULL` and Wren will report that as a runtime error.
|
||||
|
||||
If you don't use any `import` statements, you can leave this `NULL`.
|
||||
|
||||
### `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:
|
||||
|
||||
:::c
|
||||
void write(WrenVM* vm, const char* text)
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
void error(
|
||||
WrenVM* vm,
|
||||
WrenErrorType type,
|
||||
const char* module,
|
||||
int line,
|
||||
const char* message)
|
||||
|
||||
The `type` parameter is one of:
|
||||
|
||||
:::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;
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
void* reallocate(void* memory, size_t newSize)
|
||||
|
||||
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 href="storing-c-data.html">← Storing C Data</a>
|
||||
@ -1,182 +0,0 @@
|
||||
^title Embedding
|
||||
|
||||
Wren is designed to be a scripting language that lives inside a host
|
||||
application, so the embedding API is as important as any of its language
|
||||
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 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 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. 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
|
||||
defines the *maximum* performance you can get out of the system. It's the
|
||||
bottom of the stack, so there's no way for a user to optimize around it if
|
||||
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
|
||||
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, 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 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—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 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,
|
||||
it assumes you are calling them correctly. If you invoke a Wren method from C
|
||||
that expects three arguments, it trusts that you gave it three arguments.
|
||||
|
||||
In debug builds, Wren has assertions to check as many things as it can, but in
|
||||
release builds, Wren expects you to do the right thing. This means you need to
|
||||
take care when using the embedding API, just like you do in all C code you
|
||||
write. In return, you get an API that is quite fast.
|
||||
|
||||
## Including Wren
|
||||
|
||||
There are two (well, three) ways to get the Wren VM into your program:
|
||||
|
||||
1. **Link to the static or dynamic library.** When you [build Wren][build], it
|
||||
generates both shared and static libraries in `lib` that you can link to.
|
||||
|
||||
2. **Include the source directly in your application.** If you want to include
|
||||
the source directly in your program, you don't need to run any build steps.
|
||||
Just add the source files in `src/vm` to your project. They should compile
|
||||
cleanly as C99 or C++98 or anything later.
|
||||
|
||||
[build]: ../getting-started.html
|
||||
|
||||
In either case, you also want to add `src/include` to your include path so you
|
||||
can find the [public header for Wren][wren.h]:
|
||||
|
||||
[wren.h]: https://github.com/wren-lang/wren/blob/master/src/include/wren.h
|
||||
|
||||
:::c
|
||||
#include "wren.h"
|
||||
|
||||
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,
|
||||
this header handles the differences in calling conventions between C and C++:
|
||||
|
||||
:::c
|
||||
#include "wren.hpp"
|
||||
|
||||
## 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:
|
||||
|
||||
:::c
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
|
||||
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.
|
||||
|
||||
[configuration]: configuring-the-vm.html
|
||||
|
||||
With this ready, you can create the VM:
|
||||
|
||||
:::c
|
||||
WrenVM* vm = wrenNewVM(&config);
|
||||
|
||||
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, 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
|
||||
VM, waiting to run some code!
|
||||
|
||||
## Executing Wren code
|
||||
|
||||
You execute a string of Wren source code like so:
|
||||
|
||||
:::c
|
||||
WrenInterpretResult result = wrenInterpret(
|
||||
vm,
|
||||
"my_module",
|
||||
"System.print(\"I am running in a VM!\")");
|
||||
|
||||
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 one [suspends].
|
||||
|
||||
[fiber]: ../concurrency.html
|
||||
[suspends]: ../modules/core/fiber.html#fiber.suspend()
|
||||
|
||||
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
|
||||
|
||||
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);
|
||||
|
||||
After calling that, you obviously cannot use the `WrenVM*` you passed to it
|
||||
again. It's dead.
|
||||
|
||||
Note that Wren will yell at you if you still have any live [WrenHandle][handle]
|
||||
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.
|
||||
|
||||
[handle]: slots-and-handles.html#handles
|
||||
|
||||
Next, we'll learn to make that VM do useful stuff...
|
||||
|
||||
<a class="right" href="slots-and-handles.html">Slots and Handles →</a>
|
||||
@ -1,258 +0,0 @@
|
||||
^title Slots and Handles
|
||||
|
||||
With `wrenInterpret()`, we can execute code, but that code can't do anything
|
||||
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
|
||||
of the VM. These functions are based on two fundamental concepts: **slots** and
|
||||
**handles**.
|
||||
|
||||
## The Slot Array
|
||||
|
||||
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.
|
||||
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 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);
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
`wrenInterpret()`, or returning from a [foreign method][].
|
||||
|
||||
[foreign method]: calling-c-from-wren.html
|
||||
|
||||
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, use:
|
||||
|
||||
:::c
|
||||
int wrenGetSlotCount(WrenVM* vm);
|
||||
|
||||
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 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);
|
||||
|
||||
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
|
||||
really much *conversion* going on, but you get the idea.)
|
||||
|
||||
[wren value]: ../values.html
|
||||
[native number type]: ../values.html#numbers
|
||||
|
||||
You can also pass string data to Wren:
|
||||
|
||||
:::c
|
||||
void wrenSetSlotBytes(WrenVM* vm, int slot,
|
||||
const char* bytes, size_t length);
|
||||
|
||||
void wrenSetSlotString(WrenVM* vm, int slot,
|
||||
const char* text);
|
||||
|
||||
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 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
|
||||
|
||||
### Reading slots
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
these return a direct pointer to the bytes managed by Wren. You should not hold
|
||||
on to this pointer for long. Wren does not promise that it won't move or free
|
||||
the data.
|
||||
|
||||
With these functions, you are going from dynamically typed Wren data to
|
||||
statically typed C. It's up to *you* to ensure that you read a value as the
|
||||
correct type. If you read a number from a slot that currently holds a string,
|
||||
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);
|
||||
|
||||
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. 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][].
|
||||
|
||||
[foreign class]: storing-c-data.html
|
||||
|
||||
### Looking up variables
|
||||
|
||||
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);
|
||||
|
||||
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. 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 [handle](#handles).
|
||||
|
||||
### Working with lists
|
||||
|
||||
The slot array is fine for moving a fixed number of objects between Wren and
|
||||
C, but sometimes you need to shuttle a larger or dynamically-sized ball of
|
||||
stuff. [List objects][lists] work well for that, so the C API lets you work
|
||||
with them directly.
|
||||
|
||||
[lists]: ../lists.html
|
||||
|
||||
You can create a new empty list from C using:
|
||||
|
||||
:::c
|
||||
void wrenSetSlotNewList(WrenVM* vm, int slot);
|
||||
|
||||
It stores the resulting list in the given slot. If you have a list in a
|
||||
slot—either one you created from C or from Wren—you can add elements
|
||||
to it using:
|
||||
|
||||
:::c
|
||||
void wrenInsertInList(WrenVM* vm, int listSlot, int index,
|
||||
int elementSlot);
|
||||
|
||||
That's a lot of int parameters:
|
||||
|
||||
* `listSlot` is the slot where the list object is stored. That's the list you'll
|
||||
be modifying. If you created the list from C, it will be the slot you passed
|
||||
to `wrenSetSlotNewList()`.
|
||||
|
||||
* `index` is the index within the list where you want to insert the element.
|
||||
Just like from within Wren, you can use a negative number to count back from
|
||||
the end, so `-1` appends to the list.
|
||||
|
||||
* `elementSlot` identifies the slot where the value you want to insert in the
|
||||
list can be found.
|
||||
|
||||
This API means getting a value from C into a list is a two step operation. First
|
||||
you move the value into a slot, then you take it from the slot and insert it in
|
||||
the list. This is kind of tedious, but it lets us use the same set of functions
|
||||
for moving values into slots of each primitive type. Otherwise, we'd need
|
||||
`wrenInsertInListDouble()`, `wrenInsertInListBool()`, etc.
|
||||
|
||||
## Handles
|
||||
|
||||
Slots are pretty good for shuttling primitive data between C and Wren, but they
|
||||
have two limitations:
|
||||
|
||||
1. **They are short-lived.** As soon as you execute some more Wren code, the
|
||||
slot array is invalidated. You can't use a slot to persistently keep track
|
||||
of some object.
|
||||
|
||||
2. **They only support primitive types.** A slot can hold a value of any type,
|
||||
but the C API we've seen so far doesn't let you *do* anything with values
|
||||
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 handles. A handle wraps a reference to an object of
|
||||
any kind—strings, numbers, instances of classes, collections, whatever.
|
||||
You create a handle using this:
|
||||
|
||||
:::c
|
||||
WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
|
||||
|
||||
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. You can send that wrapped object
|
||||
back to Wren by calling:
|
||||
|
||||
:::c
|
||||
void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
|
||||
|
||||
Note that this doesn't invalidate your WrenHandle. You can still keep using it.
|
||||
|
||||
### Retaining and releasing handles
|
||||
|
||||
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 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. 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);
|
||||
|
||||
This does not immediately delete the wrapped object—after all, there may
|
||||
be other references to the same object in the program. It just invalidates the
|
||||
WrenHandle wrapper itself. After you call this, you cannot use that pointer
|
||||
again.
|
||||
|
||||
You must release every WrenHandle you've created before shutting down the VM.
|
||||
Wren warns you if you don't, since it implies you've probably leaked a resource
|
||||
somewhere.
|
||||
|
||||
Now we know how to pass values between Wren and C, but we don't know how to
|
||||
actually *do* anything with them. Next, we'll learn how to use slots to pass
|
||||
parameters to a Wren method from C...
|
||||
|
||||
<a class="right" href="calling-wren-from-c.html">Calling Wren from C →</a>
|
||||
<a href="index.html">← Introduction</a>
|
||||
@ -1,371 +0,0 @@
|
||||
^title Storing C Data
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
[foreign methods]: http://localhost:8000/embedding/calling-c-from-wren.html
|
||||
|
||||
## Defining a Foreign Class
|
||||
|
||||
You define one like so:
|
||||
|
||||
:::wren
|
||||
foreign class Point {
|
||||
// ...
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
:::c
|
||||
WrenForeignClassMethods bindForeignClass(
|
||||
WrenVM* vm, const char* module, const char* className);
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
typedef struct
|
||||
{
|
||||
WrenForeignMethodFn allocate;
|
||||
WrenFinalizerFn finalize;
|
||||
} WrenForeignClassMethods;
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
void allocate(WrenVM* vm);
|
||||
|
||||
## 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
|
||||
|
||||
:::c
|
||||
void* wrenSetSlotNewForeign(WrenVM* vm,
|
||||
int slot, int classSlot, size_t size);
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
void* data = wrenSetSlotNewForeign(vm, 0, 0, 8);
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
void* wrenGetSlotForeign(WrenVM* vm, int slot);
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
void finalize(void* data);
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
foreign class File {
|
||||
construct create(path) {}
|
||||
|
||||
foreign write(text)
|
||||
foreign close()
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
:::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;
|
||||
}
|
||||
|
||||
### Binding the foreign class
|
||||
|
||||
We give the VM two callbacks. The first is for wiring up the foreign class
|
||||
itself:
|
||||
|
||||
:::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;
|
||||
}
|
||||
|
||||
When our binding callback is invoked for the File class, we return the allocate
|
||||
and finalize functions the VM should call. Allocation looks like:
|
||||
|
||||
:::c
|
||||
#include <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");
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
void fileFinalize(void* data)
|
||||
{
|
||||
closeFile((FILE**) data);
|
||||
}
|
||||
|
||||
It uses this little utility function:
|
||||
|
||||
:::c
|
||||
static void closeFile(FILE** file)
|
||||
{
|
||||
// Already closed.
|
||||
if (*file == NULL) return;
|
||||
|
||||
fclose(*file);
|
||||
*file = NULL;
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
:::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;
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
:::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);
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
:::c
|
||||
void fileClose(WrenVM* vm)
|
||||
{
|
||||
FILE** file = (FILE**)wrenGetSlotForeign(vm, 0);
|
||||
closeFile(file);
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
var file = File.create("some/path.txt")
|
||||
file.write("some text")
|
||||
file.close()
|
||||
|
||||
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 →</a>
|
||||
<a href="calling-c-from-wren.html">← Calling C from Wren</a>
|
||||
@ -1,82 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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="embedding">
|
||||
<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">
|
||||
<section>
|
||||
<h2>embedding</h2>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<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>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><h2>embedding</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<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>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,173 +0,0 @@
|
||||
^title Error Handling
|
||||
|
||||
Errors come in a few fun flavors.
|
||||
|
||||
## Syntax errors
|
||||
|
||||
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
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
scripting languages, Wren tries to help you find your errors as soon as
|
||||
possible when it can.
|
||||
|
||||
If it starts running your code, you can be sure you don't have any errors
|
||||
related to syntax or variable scope.
|
||||
|
||||
## Runtime errors
|
||||
|
||||
Alas, just fixing all of the "compile-time" errors doesn't mean your code does
|
||||
what you want. Your program may still have errors that can't be detected
|
||||
statically. Since they can't be found until your code is run, they're called
|
||||
"runtime" errors.
|
||||
|
||||
Most runtime errors come from the VM itself. They arise from code trying to
|
||||
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() {}
|
||||
}
|
||||
|
||||
var foo = Foo.new()
|
||||
foo.someRandomMethod
|
||||
|
||||
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
|
||||
plugging away after a runtime error has occurred. A runtime error implies
|
||||
there's a bug in your code and it wants to draw your attention to it. To help
|
||||
you out, it prints a stack trace showing where in the code the error occurred,
|
||||
and all of the method calls that led to it.
|
||||
|
||||
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"]
|
||||
|
||||
This exits with:
|
||||
|
||||
:::text
|
||||
Subscript must be a number or a range.
|
||||
[main line 2] in (script)
|
||||
|
||||
These are the two most common kinds of runtime errors, but there are others.
|
||||
Stuff like out of bounds errors on lists, calling a function with the wrong
|
||||
number of arguments, etc.
|
||||
|
||||
## Handling runtime errors
|
||||
|
||||
Most of the time, runtime errors indicate a bug in your code and the best
|
||||
solution is to fix the bug. However, sometimes it's useful to be able to handle
|
||||
them at, uh, runtime.
|
||||
|
||||
To keep the language simpler, Wren does not have exception handling. Instead, it
|
||||
takes advantage of [fibers][] for handling errors. When a runtime error occurs,
|
||||
the current fiber is aborted. Normally, Wren will also abort any fibers that
|
||||
invoked that one, all the way to the main fiber, and then exit the VM.
|
||||
|
||||
[fibers]: concurrency.html
|
||||
|
||||
However, you can run a fiber using the `try` method. If a runtime error occurs
|
||||
in the called fiber, the error is captured and the `try` method returns the
|
||||
error message as a string.
|
||||
|
||||
For example, if you run this program:
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {
|
||||
123.badMethod
|
||||
}
|
||||
|
||||
var error = fiber.try()
|
||||
System.print("Caught error: " + error)
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
chain looking for a `try` call, so this can also be used to capture runtime
|
||||
errors generated in fibers that are invoked by the one you called `try` on.
|
||||
|
||||
## Creating runtime errors
|
||||
|
||||
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")
|
||||
|
||||
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
|
||||
indicate *bugs*—places where the code itself is incorrect. But some
|
||||
errors indicate that the code simply couldn't accomplish its task for
|
||||
unforeseeable reasons. We'll call these "failures".
|
||||
|
||||
Consider a program that reads in a string of input from the user and parses it
|
||||
to a number. Many strings are not valid numbers, so this parsing can fail. The
|
||||
only way the program could prevent that failure is by validating the string
|
||||
before its parsed, but validating that a string is a number is pretty much the
|
||||
same thing as parsing it.
|
||||
|
||||
For cases like this where failure can occur and the program *will* want to
|
||||
handle it, fibers and `try()` are too coarse-grained to work with. Instead,
|
||||
these operations will indicate failure by *returning* some sort of error
|
||||
indication.
|
||||
|
||||
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 →</a>
|
||||
<a href="concurrency.html">← Concurrency</a>
|
||||
@ -1,168 +0,0 @@
|
||||
^title Functions
|
||||
|
||||
No self-respecting language today can get by without functions—first
|
||||
class little bundles of code. Since Wren is object-oriented, most of your code
|
||||
will live in methods on classes, but free-floating functions are still
|
||||
eminently handy.
|
||||
|
||||
Functions are objects like everything else in Wren, instances of the `Fn`
|
||||
class.
|
||||
|
||||
## Block arguments
|
||||
|
||||
Most of the time you create a function just to pass it to some method. For
|
||||
example, if you want to filter a [list](lists.html) by some criteria, you'll
|
||||
call its `where` method, passing in a function that defines the predicate
|
||||
you're filtering on.
|
||||
|
||||
Since that's the most common usage pattern, Wren's syntax optimizes for that.
|
||||
Taking a page from Ruby, a function is created by passing a *block argument* to
|
||||
a method. At its simplest, it looks like this:
|
||||
|
||||
:::wren
|
||||
blondie.callMe {
|
||||
System.print("This is the body!")
|
||||
}
|
||||
|
||||
Here we're invoking the `callMe` method on `blondie`. We're passing one
|
||||
argument, a function whose body is the
|
||||
following [block](syntax.html#blocks)—everything between that pair of
|
||||
curly braces.
|
||||
|
||||
Methods that take a block argument receive it as a normal parameter. `callMe`
|
||||
could be defined like so:
|
||||
|
||||
:::wren
|
||||
class Blondie {
|
||||
callMe(fn) {
|
||||
// Call it...
|
||||
}
|
||||
}
|
||||
|
||||
var blondie = Blondie.new()
|
||||
|
||||
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())
|
||||
|
||||
## 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:
|
||||
|
||||
:::wren
|
||||
blondie.callMe {|first, last|
|
||||
System.print("Hi, " + first + " " + last + "!")
|
||||
}
|
||||
|
||||
Here we're passing a function to `callMe` that takes two parameters, `first` and
|
||||
`last`. They are passed to the function when it's called:
|
||||
|
||||
:::wren
|
||||
class Blondie {
|
||||
callMe(fn) {
|
||||
fn.call("Debbie", "Harry")
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
## Returning values
|
||||
|
||||
The body of a function is a [block](syntax.html#blocks). If it is a single
|
||||
expression—more precisely if there is no newline after the `{` or
|
||||
parameter list—then the function implicitly returns the value of the
|
||||
expression.
|
||||
|
||||
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" }
|
||||
|
||||
Fn.new {
|
||||
return "return value"
|
||||
}
|
||||
|
||||
## Closures
|
||||
|
||||
As you expect, functions are closures—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 }
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
<br><hr>
|
||||
<a class="right" href="classes.html">Classes →</a>
|
||||
<a href="variables.html">← Variables</a>
|
||||
@ -1,107 +0,0 @@
|
||||
^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:
|
||||
|
||||
* **The virtual machine.** This is the core chunk of C 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.
|
||||
|
||||
* **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—file IO,
|
||||
networking, stuff like that. It depends on [libuv][] for that
|
||||
functionality.
|
||||
|
||||
[embedded]: embedding
|
||||
[libuv]: http://libuv.org/
|
||||
|
||||
If you're on a Unix or Mac and you can rock a command line, it's just:
|
||||
|
||||
:::sh
|
||||
$ git clone https://github.com/wren-lang/wren.git
|
||||
$ cd wren
|
||||
$ make
|
||||
$ ./wren
|
||||
|
||||
This builds both the VM and the CLI. 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/`.
|
||||
|
||||
For Mac users, there is also an XCode project under `util/xcode`. For
|
||||
Windows brethren, `util/vs2017` 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.
|
||||
|
||||
If you only want to build the VM, you can do:
|
||||
|
||||
:::sh
|
||||
$ make vm
|
||||
|
||||
This compiles the VM to static and shared libraries.
|
||||
|
||||
## Interactive mode
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
System.print("Hello, world!")
|
||||
|
||||
Or a little more exciting:
|
||||
|
||||
:::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.
|
||||
|
||||
## 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
|
||||
the following work:
|
||||
|
||||
* **Ask on the [Wren mailing list][list].**
|
||||
* Tell me on twitter at [@munificentbob][twitter].
|
||||
* [File a ticket][issue] at [the GitHub repo][repo].
|
||||
* Send a pull request.
|
||||
* Email me at [`robert@stuffwithstuff.com`](mailto:robert@stuffwithstuff.com).
|
||||
|
||||
[list]: https://groups.google.com/forum/#!forum/wren-lang
|
||||
[twitter]: https://twitter.com/intent/user?screen_name=munificentbob
|
||||
[issue]: https://github.com/wren-lang/wren/issues
|
||||
[repo]: https://github.com/wren-lang/wren
|
||||
@ -1,62 +0,0 @@
|
||||
^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!")
|
||||
|
||||
class Wren {
|
||||
flyTo(city) {
|
||||
System.print("Flying to %(city)")
|
||||
}
|
||||
}
|
||||
|
||||
var adjectives = Fiber.new {
|
||||
["small", "clean", "fast"].each {|word| Fiber.yield(word) }
|
||||
}
|
||||
|
||||
while (!adjectives.isDone) System.print(adjectives.call())
|
||||
|
||||
|
||||
* **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
|
||||
*dense*. It is readable and [lovingly-commented][nan].
|
||||
|
||||
* **Wren is fast.** A fast single-pass compiler to tight bytecode, and a
|
||||
compact object representation help Wren [compete with other dynamic
|
||||
languages][perf].
|
||||
|
||||
* **Wren is class-based.** There are lots of scripting languages out there,
|
||||
but many have unusual or non-existent object models. Wren places
|
||||
[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
|
||||
coroutines.
|
||||
|
||||
* **Wren is a scripting language.** Wren is intended for embedding in
|
||||
applications. It has no dependencies, a small standard library,
|
||||
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]!
|
||||
|
||||
[syntax]: syntax.html
|
||||
[src]: https://github.com/wren-lang/wren/tree/master/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/
|
||||
[contribute]: contributing.html
|
||||
@ -1,126 +0,0 @@
|
||||
^title Lists
|
||||
|
||||
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]
|
||||
|
||||
Here, we've created a list of three elements. Notice that the elements don't
|
||||
have to be the same type.
|
||||
|
||||
## Accessing elements
|
||||
|
||||
You can access an element from a list by calling the [subscript
|
||||
operator][] on it with the index of the
|
||||
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
|
||||
|
||||
Negative indices counts backwards from the end:
|
||||
|
||||
:::wren
|
||||
System.print(hirsute[-1]) //> goatee
|
||||
System.print(hirsute[-2]) //> 'stache
|
||||
|
||||
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
|
||||
|
||||
## 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]
|
||||
|
||||
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
|
||||
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]
|
||||
|
||||
## 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
|
||||
|
||||
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) //> 5
|
||||
|
||||
You can insert a new element at a specific position using `insert`:
|
||||
|
||||
:::wren
|
||||
hirsute.insert(2, "soul patch")
|
||||
|
||||
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
|
||||
make room for it.
|
||||
|
||||
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]
|
||||
|
||||
## Adding lists together
|
||||
|
||||
Lists have the ability to be added together via the `+` operator. This is often known as concatenation.
|
||||
|
||||
:::wren
|
||||
var letters = ["a", "b", "c"]
|
||||
var other = ["d", "e", "f"]
|
||||
var combined = letters + other
|
||||
System.print(combined) //> [a, b, c, d, e, f]
|
||||
|
||||
## Removing elements
|
||||
|
||||
The opposite of `insert` is `removeAt`. It removes a single element from a
|
||||
given position in the list. All following items are shifted up to fill in the
|
||||
gap:
|
||||
|
||||
:::wren
|
||||
var letters = ["a", "b", "c", "d"]
|
||||
letters.removeAt(1)
|
||||
System.print(letters) //> [a, c, d]
|
||||
|
||||
The `removeAt` method returns the removed item:
|
||||
|
||||
:::wren
|
||||
System.print(letters.removeAt(1)) //> c
|
||||
|
||||
If you want to remove everything from the list, you can clear it:
|
||||
|
||||
:::wren
|
||||
hirsute.clear()
|
||||
System.print(hirsute) //> []
|
||||
|
||||
<br><hr>
|
||||
<a class="right" href="maps.html">Maps →</a>
|
||||
<a href="values.html">← Values</a>
|
||||
@ -1,137 +0,0 @@
|
||||
^title Maps
|
||||
|
||||
A map is an *associative* collection. It holds a set of entries, each of which
|
||||
maps a *key* to a *value*. The same data structure has a variety of names in
|
||||
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"
|
||||
}
|
||||
|
||||
This creates a map that associates the first name of each Beatle with his last
|
||||
name. Syntactically, in a map literal, keys can be any literal, a variable
|
||||
name, or a parenthesized expression. Values can be any expression. Here, we're
|
||||
using string literals for both keys and values.
|
||||
|
||||
*Semantically*, values can be any object, and multiple keys may map to the same
|
||||
value. Keys have a few limitations. They must be one of the immutable built-in
|
||||
[value types][] in Wren. That means a number, string, range, bool, or `null`.
|
||||
You can also use a [class object][] as a key.
|
||||
|
||||
[value types]: values.html
|
||||
[class object]: classes.html
|
||||
|
||||
The reason for this limitation—and the reason maps are called "*hash*
|
||||
tables" in other languages—is that each key is used to generate a numeric
|
||||
*hash code*. This lets a map locate the value associated with a key in constant
|
||||
time, even in very large maps. Since Wren only knows how to hash certain
|
||||
built-in types, only those can be used as keys.
|
||||
|
||||
## Adding entries
|
||||
|
||||
You add new key-value pairs to the map using the [subscript operator][]:
|
||||
|
||||
[subscript operator]: method-calls.html#subscripts
|
||||
|
||||
:::wren
|
||||
var capitals = {}
|
||||
capitals["Georgia"] = "Atlanta"
|
||||
capitals["Idaho"] = "Boise"
|
||||
capitals["Maine"] = "Augusta"
|
||||
|
||||
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.
|
||||
|
||||
## Looking up values
|
||||
|
||||
To find the value associated with some key, again you use your friend the
|
||||
subscript operator:
|
||||
|
||||
:::wren
|
||||
System.print(capitals["Idaho"]) //> Boise
|
||||
|
||||
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
|
||||
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}
|
||||
|
||||
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
|
||||
|
||||
You can see how many entries a map contains using `count`:
|
||||
|
||||
:::wren
|
||||
System.print(capitals.count) //> 3
|
||||
|
||||
## 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
|
||||
|
||||
If the key was found, this returns the value that was associated with it:
|
||||
|
||||
:::wren
|
||||
System.print(capitals.remove("Georgia")) //> Atlanta
|
||||
|
||||
If the key wasn't in the map to begin with, `remove()` just returns `null`.
|
||||
|
||||
If you want to remove *everything* from the map, like with [lists][], you call
|
||||
`clear()`:
|
||||
|
||||
[lists]: lists.html
|
||||
|
||||
:::wren
|
||||
capitals.clear()
|
||||
System.print(capitals.count) //> 0
|
||||
|
||||
## 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`.
|
||||
|
||||
The first returns a [Sequence][] that [iterates][] over all of the keys in the
|
||||
map, and the second returns one that iterates over the values.
|
||||
|
||||
[sequence]: modules/core/sequence.html
|
||||
[iterates]: control-flow.html#the-iterator-protocol
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
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])
|
||||
}
|
||||
|
||||
This program prints the three states and their birds. However, the *order*
|
||||
that they are printed isn't defined. Wren makes no promises about what order
|
||||
keys and values are iterated in when you use these methods. All it promises is
|
||||
that every entry will appear exactly once.
|
||||
|
||||
<br><hr>
|
||||
<a class="right" href="method-calls.html">Method Calls →</a>
|
||||
<a href="lists.html">← Lists</a>
|
||||
@ -1,182 +0,0 @@
|
||||
^title Method Calls
|
||||
|
||||
Wren is deeply object oriented, so most code consists of invoking methods on
|
||||
objects, usually something like this:
|
||||
|
||||
:::wren
|
||||
System.print("Heyoo!") //> Heyoo!
|
||||
|
||||
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")
|
||||
|
||||
The argument list can also be empty:
|
||||
|
||||
:::wren
|
||||
list.clear()
|
||||
|
||||
The VM executes a method call like so:
|
||||
|
||||
1. Evaluate the receiver and arguments from left to right.
|
||||
2. Look up the method on the receiver's [class][].
|
||||
3. Invoke it, passing in the argument values.
|
||||
|
||||
[class]: classes.html
|
||||
|
||||
## Signature
|
||||
|
||||
Unlike most other dynamically-typed languages, in Wren a class can have multiple
|
||||
methods with the same *name*, as long as they have different *signatures*. The
|
||||
signature includes the method's name along with the number of arguments it
|
||||
takes. In technical terms, this means you can *overload by arity*.
|
||||
|
||||
For example, the [Random][] class has two methods for getting a random integer.
|
||||
One takes a minimum and maximum value and returns a value in that range. The
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
need optional parameters or any kind of control flow to handle the different
|
||||
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..." 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
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
list.clear()
|
||||
|
||||
* If the method supports multiple arities, make the zero-parameter case a `()`
|
||||
method to be consistent with the other versions:
|
||||
|
||||
:::wren
|
||||
Fiber.yield()
|
||||
Fiber.yield("value")
|
||||
|
||||
* Otherwise, it can probably be a getter.
|
||||
|
||||
## Setters
|
||||
|
||||
A getter lets an object expose a public "property" that you can *read*.
|
||||
Likewise, a *setter* lets you write to a property:
|
||||
|
||||
:::wren
|
||||
person.height = 74 // Grew up!
|
||||
|
||||
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 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. 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
|
||||
! ~ -
|
||||
|
||||
They are just method calls on their operand without any other arguments. An
|
||||
expression like `!possible` means "call the `!` method on `possible`".
|
||||
|
||||
We also have a slew of infix operators—they have operands on both sides.
|
||||
They are:
|
||||
|
||||
:::wren
|
||||
* / % + - .. ... << >> < <= > >= == != & ^ | is
|
||||
|
||||
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
|
||||
semantically interpreted as "invoke the `+(_)` method on `a`, passing it `b`".
|
||||
|
||||
Note that `-` is both a prefix and an infix operator. Since they have different
|
||||
signatures (`-` and `-(_)`), there's no ambiguity between them.
|
||||
|
||||
Most of these are probably familiar already. The `..` and `...` operators are
|
||||
"range" operators. The number type implements those to create [range][]
|
||||
objects, but they are method calls like other operators.
|
||||
|
||||
[range]: values.html#ranges
|
||||
|
||||
The `is` keyword is a "type test" operator. The base [Object][] class implements
|
||||
it to tell if an object is an instance of a given class. You'll rarely need to,
|
||||
but you can override `is` in your own classes. That can be useful for things
|
||||
like mocks or proxies where you want an object to masquerade as a certain class.
|
||||
|
||||
[object]: modules/core/object.html
|
||||
|
||||
## Subscripts
|
||||
|
||||
Another familiar syntax from math class 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".
|
||||
|
||||
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]
|
||||
|
||||
These examples are subscript "getters", and there are also
|
||||
corresponding *subscript setters*:
|
||||
|
||||
:::wren
|
||||
list[0] = "item"
|
||||
map["key"] = "value"
|
||||
|
||||
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 →</a>
|
||||
<a href="maps.html">← Maps</a>
|
||||
@ -1,290 +0,0 @@
|
||||
^title Modularity
|
||||
|
||||
Once you start writing programs that are more than little toys, you quickly run
|
||||
into two problems:
|
||||
|
||||
1. You want to break them down into multiple smaller files to make it easier to
|
||||
find your way around them.
|
||||
|
||||
2. You want to reuse pieces of them across different programs.
|
||||
|
||||
To address those, Wren has a simple module system. A file containing Wren code
|
||||
defines a *module*. A module can use the code defined in another module by
|
||||
*importing* it. You can break big programs into smaller modules that you
|
||||
import, and you can reuse code by having multiple programs share the use of a
|
||||
single module.
|
||||
|
||||
Wren does not have a single global scope. Instead, each module has its own
|
||||
top-level scope independent of all other modules. This means, for example, that
|
||||
two modules can define a top-level variable with the same name without causing
|
||||
a name collision. Each module is, well, modular.
|
||||
|
||||
## Importing, briefly
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
That's the basic idea. Now let's break it down into each of the steps it
|
||||
performs:
|
||||
|
||||
1. Locate the source code for the module.
|
||||
2. Execute the imported module's code.
|
||||
3. Bind new variables in the importing module to values defined in the imported
|
||||
module.
|
||||
|
||||
We'll go through each step:
|
||||
|
||||
## Locating a module
|
||||
|
||||
The first thing you need to do to import a module is actually *find* the code
|
||||
for it. The import specifies a *name*—some arbitrary string that is used
|
||||
to uniquely identify the module. The embedding application controls how that
|
||||
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;
|
||||
|
||||
// Other configuration...
|
||||
|
||||
WrenVM* vm = wrenNewVM(&config);
|
||||
|
||||
That function has this signature:
|
||||
|
||||
:::c
|
||||
char* WrenLoadModuleFn(WrenVM* vm, const char* name);
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
### The command-line loader
|
||||
|
||||
The default little command-line VM that comes with Wren 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
|
||||
|
||||
And that main module has:
|
||||
|
||||
:::wren
|
||||
import "some/module"
|
||||
|
||||
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,
|
||||
to help ensure your scripts are platform-independent. (Forward slashes are a
|
||||
valid separator on Windows, but backslashes are not valid on other OSes.)
|
||||
|
||||
## Executing the module
|
||||
|
||||
Once we have the source code for a module, we need to run it. First, the VM
|
||||
takes the [fiber][] that is executing the `import` statement in the importing
|
||||
module and pauses it.
|
||||
|
||||
[fiber]: concurrency.html
|
||||
|
||||
Then it creates a new module object—a new fresh top-level scope,
|
||||
basically—and a new fiber. It executes the new module's code in that
|
||||
fiber and scope. The module can run whatever imperative code it wants and
|
||||
define whatever top-level variables it wants.
|
||||
|
||||
When the module's code is done being executed and its fiber completes, the
|
||||
suspended fiber for the importing module is resumed. This suspending and
|
||||
resuming is recursive. So, if "a" imports "b" which imports "c", both "a" and
|
||||
"b" will be suspended while "c" is running. When "c" is done, "b" is resumed.
|
||||
Then, when "b" completes, "a" is resumed.
|
||||
|
||||
Think of it like traversing the tree of imports, one node at a time. At any
|
||||
given point in time, only one module's code is running.
|
||||
|
||||
## Binding variables
|
||||
|
||||
Once the module is done executing, the last step is to actually *import* some
|
||||
data from it. Any module can define "top-level" [variables](variables.html).
|
||||
These are simply variables declared outside of any
|
||||
[method](classes.html#methods) or [function](functions.html).
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
that name in the imported module. If a variable with that name can't be found
|
||||
in the imported module, it's a runtime error.
|
||||
|
||||
Otherwise, it gets the current value of the variable and defines a new variable
|
||||
in the importing module with the same name and value. It's worth noting that
|
||||
the importing module gets its *own* variable whose value is a snapshot of the
|
||||
value of the imported variable at the time it was imported. If either module
|
||||
later assigns to that variable, the other won't see it. It's not a "live"
|
||||
connection.
|
||||
|
||||
In practice, most top-level variables are only assigned once anyway, so this
|
||||
rarely makes a difference.
|
||||
|
||||
## Shared imports
|
||||
|
||||
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"
|
||||
|
||||
// a.wren
|
||||
import "shared"
|
||||
|
||||
// b.wren
|
||||
import "shared"
|
||||
|
||||
// shared.wren
|
||||
System.print("Shared!")
|
||||
|
||||
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
|
||||
code is only executed the *first* time it is loaded. After that, importing the
|
||||
module again just looks up the previously loaded module.
|
||||
|
||||
Internally, Wren maintains a map of every module it has previously loaded. When
|
||||
a module is imported, Wren looks for it in that map first before it calls out
|
||||
to the embedder for its source.
|
||||
|
||||
In other words, in that list of steps above, there's an implicit zeroth step:
|
||||
"See if we already loaded the module and reuse it if we did". That means the
|
||||
above program only prints "Shared!" once.
|
||||
|
||||
## Cyclic imports
|
||||
|
||||
You can even have cycles in your imports, provided you're a bit careful with
|
||||
them. The loading process, in detail, is:
|
||||
|
||||
1. See if we have already created a module with the given name.
|
||||
2. If so, use it.
|
||||
3. Otherwise, create a new module with the name and store it in the module
|
||||
registry.
|
||||
4. Create a fiber for it and execute its code.
|
||||
|
||||
Note the order of the last two steps. When a module is loaded, it is added to
|
||||
the registry *before* it is executed. This means if an import for that same
|
||||
module is reached while the module itself or one of its imports is executing,
|
||||
it will be found in the registry and the cycle is short-circuited.
|
||||
|
||||
For example:
|
||||
|
||||
:::wren
|
||||
// main.wren
|
||||
import "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")
|
||||
|
||||
This program runs successfully and prints:
|
||||
|
||||
:::text
|
||||
start a
|
||||
start b
|
||||
end b
|
||||
end a
|
||||
|
||||
Where you have to be careful is binding variables. Consider:
|
||||
|
||||
:::wren
|
||||
// main.wren
|
||||
import "a"
|
||||
|
||||
// a.wren
|
||||
import "b" for B
|
||||
var A = "a variable"
|
||||
|
||||
// b.wren
|
||||
import "a" for A
|
||||
var B = "b variable"
|
||||
|
||||
The import of "a" in b.wren will fail here. If you trace the execution, you
|
||||
get:
|
||||
|
||||
1. Execute `import "a"` in "main.wren". That suspends "main.wren".
|
||||
2. Execute `import "b"` in "a.wren". That suspends "a.wren".
|
||||
3. Execute `import "a"` in "b.wren". Since "a" is already in the module map,
|
||||
this does *not* suspend it.
|
||||
|
||||
Instead, we look for a variable named `A` in that module. But it hasn't been
|
||||
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"
|
||||
|
||||
// a.wren
|
||||
var A = "a variable"
|
||||
import "b" for B
|
||||
|
||||
// b.wren
|
||||
import "a" for A
|
||||
var B = "b variable"
|
||||
|
||||
Now when we run it, we get:
|
||||
|
||||
1. Execute `import "a"` in "main.wren". That suspends "main.wren".
|
||||
2. Define `A` in "a.wren".
|
||||
2. Execute `import "b"` in "a.wren". That suspends "a.wren".
|
||||
3. Execute `import "a"` in "b.wren". Since "a" is already in the module map,
|
||||
this does *not* suspend it. It looks up `A`, which has already been defined,
|
||||
and binds it.
|
||||
4. Define `B` in "b.wren".
|
||||
5. Complete "b.wren".
|
||||
6. Look up `B` in "b.wren" and bind it in "a.wren".
|
||||
7. Resume "a.wren".
|
||||
|
||||
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.
|
||||
|
||||
<br><hr>
|
||||
<a href="error-handling.html">← Error Handling</a>
|
||||
@ -1,19 +0,0 @@
|
||||
^title Bool Class
|
||||
|
||||
Boolean [values][]. There are two instances, `true` and `false`.
|
||||
|
||||
[values]: ../../values.html
|
||||
|
||||
## Methods
|
||||
|
||||
### **!** operator
|
||||
|
||||
Returns the logical complement of the value.
|
||||
|
||||
:::wren
|
||||
System.print(!true) //> false
|
||||
System.print(!false) //> true
|
||||
|
||||
### toString
|
||||
|
||||
The string representation of the value, either `"true"` or `"false"`.
|
||||
@ -1,29 +0,0 @@
|
||||
^title Class Class
|
||||
|
||||
**TODO**
|
||||
|
||||
## Methods
|
||||
|
||||
### **name**
|
||||
|
||||
The name of the class.
|
||||
|
||||
### **supertype**
|
||||
|
||||
The superclass of this class.
|
||||
|
||||
:::wren
|
||||
class Crustacean {}
|
||||
class Crab is Crustacean {}
|
||||
|
||||
System.print(Crab.supertype) //> Crustacean
|
||||
|
||||
A class with no explicit superclass implicitly inherits Object:
|
||||
|
||||
:::wren
|
||||
System.print(Crustacean.supertype) //> Object
|
||||
|
||||
Object forms the root of the class hierarchy and has no supertype:
|
||||
|
||||
:::wren
|
||||
System.print(Object.supertype) //> null
|
||||
@ -1,176 +0,0 @@
|
||||
^title Fiber Class
|
||||
|
||||
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:
|
||||
|
||||
:::wren
|
||||
Fiber.abort("Something bad happened.")
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
`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.
|
||||
|
||||
[fn]: fn.html
|
||||
|
||||
### Fiber.**suspend**()
|
||||
|
||||
Pauses the current fiber, and stops the interpreter. Control returns to the
|
||||
host application.
|
||||
|
||||
Typically, you store a reference to the fiber using `Fiber.current` before
|
||||
calling this. The fiber can be resumed later by calling or transferring to that
|
||||
reference. If there are no references to it, it is eventually garbage collected.
|
||||
|
||||
Much like `yield()`, returns the value passed to `call()` or `transfer()` when
|
||||
the fiber is resumed.
|
||||
|
||||
### Fiber.**yield**()
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
fiber.call() //> Before yield
|
||||
System.print("After call") //> After call
|
||||
fiber.call() //> After yield
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fiber.call() // Run until the first yield.
|
||||
fiber.call("value") // Resume the fiber.
|
||||
|
||||
If it was resumed by calling `call()` or `transfer()` with no argument, it
|
||||
returns `null`.
|
||||
|
||||
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")
|
||||
|
||||
### 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")
|
||||
}
|
||||
|
||||
System.print(fiber.call()) //> value
|
||||
|
||||
## Methods
|
||||
|
||||
### **call**()
|
||||
|
||||
Starts or resumes the fiber if it is in a paused state. Equivalent to:
|
||||
|
||||
:::wren
|
||||
fiber.call(null)
|
||||
|
||||
### **call**(value)
|
||||
|
||||
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 {|param|
|
||||
System.print(param) //> begin
|
||||
}
|
||||
|
||||
fiber.call("begin")
|
||||
|
||||
If the fiber is being resumed, `value` becomes the returned value of the fiber's
|
||||
call to `yield`.
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {
|
||||
System.print(Fiber.yield()) //> resume
|
||||
}
|
||||
|
||||
fiber.call()
|
||||
fiber.call("resume")
|
||||
|
||||
### **error**
|
||||
|
||||
The error message that was passed when aborting the fiber, or `null` if the
|
||||
fiber has not been aborted.
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {
|
||||
123.badMethod
|
||||
}
|
||||
|
||||
fiber.try()
|
||||
System.print(fiber.error) //> Num does not implement method 'badMethod'.
|
||||
|
||||
### **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.
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {
|
||||
123.badMethod
|
||||
}
|
||||
|
||||
var error = fiber.try()
|
||||
System.print("Caught error: " + error)
|
||||
|
||||
If the called fiber raises an error, it can no longer be used.
|
||||
|
||||
### **transfer**()
|
||||
|
||||
**TODO**
|
||||
|
||||
### **transfer**(value)
|
||||
|
||||
**TODO**
|
||||
|
||||
### **transferError**(error)
|
||||
|
||||
**TODO**
|
||||
@ -1,47 +0,0 @@
|
||||
^title Fn Class
|
||||
|
||||
A first class function—an object that wraps an executable chunk of code.
|
||||
[Here][functions] is a friendly introduction.
|
||||
|
||||
[functions]: ../../functions.html
|
||||
|
||||
## Static Methods
|
||||
|
||||
### Fn.**new**(function)
|
||||
|
||||
Creates a new function from... `function`. Of course, `function` is already a
|
||||
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")
|
||||
}
|
||||
|
||||
It is a runtime error if `function` is not a function.
|
||||
|
||||
## Methods
|
||||
|
||||
### **arity**
|
||||
|
||||
The number of arguments the function requires.
|
||||
|
||||
:::wren
|
||||
System.print(Fn.new {}.arity) //> 0
|
||||
System.print(Fn.new {|a, b, c| a }.arity) //> 3
|
||||
|
||||
### **call**(args...)
|
||||
|
||||
Invokes the function with the given arguments.
|
||||
|
||||
:::wren
|
||||
var fn = Fn.new { |arg|
|
||||
System.print(arg) //> Hello world
|
||||
}
|
||||
|
||||
fn.call("Hello world")
|
||||
|
||||
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
|
||||
ignored.
|
||||
@ -1,24 +0,0 @@
|
||||
^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
|
||||
@ -1,122 +0,0 @@
|
||||
^title List Class
|
||||
|
||||
Extends [Sequence](sequence.html).
|
||||
|
||||
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 nonnegative integer.
|
||||
|
||||
### List.**new**()
|
||||
|
||||
Creates a new empty list. Equivalent to `[]`.
|
||||
|
||||
## Methods
|
||||
|
||||
### **add**(item)
|
||||
|
||||
Appends `item` to the end of the list.
|
||||
|
||||
### **clear**()
|
||||
|
||||
Removes all elements from the list.
|
||||
|
||||
### **count**
|
||||
|
||||
The number of elements in the list.
|
||||
|
||||
### **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]
|
||||
|
||||
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]
|
||||
|
||||
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]
|
||||
|
||||
Returns the inserted item.
|
||||
|
||||
:::wren
|
||||
System.print(["a", "c"].insert(1, "b")) //> b
|
||||
|
||||
It is a runtime error if the index is not an integer or is out of bounds.
|
||||
|
||||
### **iterate**(iterator), **iteratorValue**(iterator)
|
||||
|
||||
Implements the [iterator protocol][] for iterating over the elements in the
|
||||
list.
|
||||
|
||||
[iterator protocol]: ../../control-flow.html#the-iterator-protocol
|
||||
|
||||
### **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]
|
||||
|
||||
Returns the removed item.
|
||||
|
||||
:::wren
|
||||
System.print(["a", "b", "c"].removeAt(1)) //> b
|
||||
|
||||
It is a runtime error if the index is not an integer or is out of bounds.
|
||||
|
||||
### **[**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
|
||||
|
||||
It is a runtime error if the index is not an integer 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]
|
||||
|
||||
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 a `List`.
|
||||
|
||||
:::wren
|
||||
var letters = ["a", "b", "c"]
|
||||
var other = ["d", "e", "f"]
|
||||
var combined = letters + other
|
||||
System.print(combined) //> [a, b, c, d, e, f]
|
||||
@ -1,57 +0,0 @@
|
||||
^title Map Class
|
||||
|
||||
An associative collection that maps keys to values. More details [here](../../maps.html).
|
||||
|
||||
## Methods
|
||||
|
||||
### **clear**()
|
||||
|
||||
Removes all entries from the map.
|
||||
|
||||
### **containsKey**(key)
|
||||
|
||||
Returns `true` if the map contains `key` or `false` otherwise.
|
||||
|
||||
### **count**
|
||||
|
||||
The number of entries in the map.
|
||||
|
||||
### **keys**
|
||||
|
||||
A [Sequence](sequence.html) that can be used to iterate over the keys in the
|
||||
map. Note that iteration order is undefined. All keys will be iterated over,
|
||||
but may be in any order, and may even change between invocations of Wren.
|
||||
|
||||
### **remove**(key)
|
||||
|
||||
Removes `key` and the value associated with it from the map. Returns the value.
|
||||
|
||||
If the key was not present, returns `null`.
|
||||
|
||||
### **values**
|
||||
|
||||
A [Sequence](sequence.html) that can be used to iterate over the values in the
|
||||
map. Note that iteration order is undefined. All values will be iterated over,
|
||||
but may be in any order, and may even change between invocations of Wren.
|
||||
|
||||
If multiple keys are associated with the same value, the value will appear
|
||||
multiple times in the sequence.
|
||||
|
||||
### **[**key**]** operator
|
||||
|
||||
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
|
||||
|
||||
### **[**key**]=**(value) operator
|
||||
|
||||
Associates `value` with `key` in the map. If `key` was already in the map, this
|
||||
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).
|
||||
@ -1,10 +0,0 @@
|
||||
^title Null Class
|
||||
|
||||
## Methods
|
||||
|
||||
### **!** operator
|
||||
|
||||
Returns `true`, since `null` is considered [false](../control-flow.html#truth).
|
||||
|
||||
:::wren
|
||||
System.print(!null) //> true
|
||||
@ -1,191 +0,0 @@
|
||||
^title Num Class
|
||||
|
||||
## Static Methods
|
||||
|
||||
### Num.**fromString**(value)
|
||||
|
||||
Attempts to parse `value` as a decimal literal and return it as an instance of
|
||||
`Num`. If the number cannot be parsed `null` will be returned.
|
||||
|
||||
It is a runtime error if `value` is not a string.
|
||||
|
||||
### Num.**pi**
|
||||
|
||||
The value of π.
|
||||
|
||||
### Num.**largest**
|
||||
|
||||
The largest representable numeric value.
|
||||
|
||||
### Num.**smallest**
|
||||
|
||||
The smallest positive representable numeric value.
|
||||
|
||||
## Methods
|
||||
|
||||
### **abs**
|
||||
|
||||
The absolute value of the number.
|
||||
|
||||
:::wren
|
||||
System.print( (-123).abs ) //> 123
|
||||
|
||||
### **acos**
|
||||
|
||||
The arc cosine of the number.
|
||||
|
||||
### **asin**
|
||||
|
||||
The arc sine of the number.
|
||||
|
||||
### **atan**
|
||||
|
||||
The arc tangent of the number.
|
||||
|
||||
### **atan**(x)
|
||||
|
||||
The arc tangent of the number when divided by `x`, using the signs of the two
|
||||
numbers to determine the quadrant of the result.
|
||||
|
||||
### **ceil**
|
||||
|
||||
Rounds the number up to the nearest integer.
|
||||
|
||||
:::wren
|
||||
System.print(1.5.ceil) //> 2
|
||||
System.print((-3.2).ceil) //> -3
|
||||
|
||||
### **cos**
|
||||
|
||||
The cosine of the number.
|
||||
|
||||
### **floor**
|
||||
|
||||
Rounds the number down to the nearest integer.
|
||||
|
||||
:::wren
|
||||
System.print(1.5.floor) //> 1
|
||||
System.print((-3.2).floor) //> -4
|
||||
|
||||
### **isInfinity**
|
||||
|
||||
Whether the number is positive or negative infinity or not.
|
||||
|
||||
:::wren
|
||||
System.print(99999.isInfinity) //> false
|
||||
System.print((1/0).isInfinity) //> true
|
||||
|
||||
### **isInteger**
|
||||
|
||||
Whether the number is an integer or has some fractional component.
|
||||
|
||||
:::wren
|
||||
System.print(2.isInteger) //> true
|
||||
System.print(2.3.isInteger) //> false
|
||||
|
||||
### **isNan**
|
||||
|
||||
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.
|
||||
|
||||
### **pow**(power)
|
||||
|
||||
Raises this number (the base) to `power`. Returns `nan` if the base is negative.
|
||||
|
||||
### **round**
|
||||
|
||||
Rounds the number to the nearest integer.
|
||||
|
||||
:::wren
|
||||
System.print(1.5.round) //> 2
|
||||
System.print((-3.2).round) //> -3
|
||||
System.print((-3.7).round) //> -4
|
||||
|
||||
### **sin**
|
||||
|
||||
The sine of the number.
|
||||
|
||||
### **sqrt**
|
||||
|
||||
The square root of the number. Returns `nan` if the number is negative.
|
||||
|
||||
### **tan**
|
||||
|
||||
The tangent of the number.
|
||||
|
||||
### **-** operator
|
||||
|
||||
Negates the number.
|
||||
|
||||
:::wren
|
||||
var a = 123
|
||||
System.print(-a) //> -123
|
||||
|
||||
### **-**(other), **+**(other), **/**(other), **\***(other) operators
|
||||
|
||||
The usual arithmetic operators you know and love. All of them do 64-bit
|
||||
floating point arithmetic. It is a runtime error if the right-hand operand is
|
||||
not a number. Wren doesn't roll with implicit conversions.
|
||||
|
||||
### **%**(denominator) operator
|
||||
|
||||
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.
|
||||
|
||||
### **<**(other), **>**(other), **<=**(other), **>=**(other) operators
|
||||
|
||||
Compares this and `other`, returning `true` or `false` based on how the numbers
|
||||
are ordered. It is a runtime error if `other` is not a number.
|
||||
|
||||
### **~** operator
|
||||
|
||||
Performs *bitwise* negation on the number. The number is first converted to a
|
||||
32-bit unsigned value, which will truncate any floating point value. The bits
|
||||
of the result of that are then negated, yielding the result.
|
||||
|
||||
### **&**(other) operator
|
||||
|
||||
Performs bitwise and 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`.
|
||||
|
||||
It is a runtime error if `other` is not a number.
|
||||
|
||||
### **|**(other) operator
|
||||
|
||||
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 one or both inputs were `true`.
|
||||
|
||||
It is a runtime error if `other` is not a number.
|
||||
|
||||
### **..**(other) operator
|
||||
|
||||
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
|
||||
|
||||
### **...**(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
|
||||
@ -1,53 +0,0 @@
|
||||
^title Object Class
|
||||
|
||||
## Static Methods
|
||||
|
||||
### **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
|
||||
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
|
||||
the exact same object in memory.
|
||||
|
||||
This is similar to the built in `==` operator in Object except that this cannot
|
||||
be overriden. It allows you to reliably access the built-in equality semantics
|
||||
even on user-defined classes.
|
||||
|
||||
## Methods
|
||||
|
||||
### **!** operator
|
||||
|
||||
Returns `false`, since most objects are considered [true][].
|
||||
|
||||
[true]: control-flow.html#truth
|
||||
|
||||
### **==**(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
|
||||
identity—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
|
||||
|
||||
It is a runtime error if `class` is not a [Class][].
|
||||
|
||||
### **toString**
|
||||
|
||||
A default string representation of the object.
|
||||
|
||||
### **type**
|
||||
|
||||
The [Class][] of the object.
|
||||
|
||||
[class]: class.html
|
||||
@ -1,57 +0,0 @@
|
||||
^title Range Class
|
||||
|
||||
A range defines a bounded range of values from a starting point to a possibly
|
||||
exclusive endpoint. [Here](../../values.html#ranges) is a friendly introduction.
|
||||
|
||||
Extends [Sequence](sequence.html).
|
||||
|
||||
## Methods
|
||||
|
||||
### **from**
|
||||
|
||||
The starting point of the range. A range may be backwards, so this can be
|
||||
greater than [to].
|
||||
|
||||
:::wren
|
||||
System.print((3..5).from) //> 3
|
||||
System.print((4..2).from) //> 4
|
||||
|
||||
### **to**
|
||||
|
||||
The endpoint of the range. If the range is inclusive, this value is included,
|
||||
otherwise it is not.
|
||||
|
||||
:::wren
|
||||
System.print((3..5).to) //> 5
|
||||
System.print((4..2).to) //> 2
|
||||
|
||||
### **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
|
||||
|
||||
### **max**
|
||||
|
||||
The maximum bound of the range. Returns either `from`, or `to`, whichever is
|
||||
greater.
|
||||
|
||||
:::wren
|
||||
System.print((3..5).max) //> 5
|
||||
System.print((4..2).max) //> 4
|
||||
|
||||
### **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
|
||||
|
||||
### **iterate**(iterator), **iteratorValue**(iterator)
|
||||
|
||||
Iterates over the range. Starts at `from` and increments by one towards `to`
|
||||
until the endpoint is reached.
|
||||
@ -1,187 +0,0 @@
|
||||
^title Sequence Class
|
||||
|
||||
An abstract base class for any iterable object. Any class that implements the
|
||||
core [iterator protocol][] can extend this to get a number of helpful methods.
|
||||
|
||||
[iterator protocol]: ../../control-flow.html#the-iterator-protocol
|
||||
|
||||
## Methods
|
||||
|
||||
### **all**(predicate)
|
||||
|
||||
Tests whether all the elements in the sequence pass the `predicate`.
|
||||
|
||||
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
|
||||
|
||||
### **any**(predicate)
|
||||
|
||||
Tests whether any element in the sequence passes the `predicate`.
|
||||
|
||||
Iterates over the sequence, passing each element to the function `predicate`.
|
||||
If it returns something [true][], stops iterating and
|
||||
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
|
||||
|
||||
### **contains**(element)
|
||||
|
||||
Returns whether the sequence contains any element equal to the given element.
|
||||
|
||||
### **count**
|
||||
|
||||
The number of elements in the sequence.
|
||||
|
||||
Unless a more efficient override is available, this will iterate over the
|
||||
sequence in order to determine how many elements it contains.
|
||||
|
||||
### **count**(predicate)
|
||||
|
||||
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
|
||||
|
||||
### **each**(function)
|
||||
|
||||
Iterates over the sequence, passing each element to the given `function`.
|
||||
|
||||
:::wren
|
||||
["one", "two", "three"].each {|word| System.print(word) }
|
||||
|
||||
### **isEmpty**
|
||||
|
||||
Returns whether the sequence contains any elements.
|
||||
|
||||
This can be more efficient that `count == 0` because this does not iterate over
|
||||
the entire sequence.
|
||||
|
||||
### **join**(separator)
|
||||
|
||||
Converts every element in the sequence to a string and then joins the results
|
||||
together into a single string, each separated by `separator`.
|
||||
|
||||
It is a runtime error if `separator` is not a string.
|
||||
|
||||
### **join**()
|
||||
|
||||
Converts every element in the sequence to a string and then joins the results
|
||||
together into a single string.
|
||||
|
||||
### **map**(transformation)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
sequence.
|
||||
|
||||
This means you can use `map(_)` for things like infinite sequences or sequences
|
||||
that have side effects when you iterate over them. But it also means that
|
||||
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]
|
||||
|
||||
### **reduce**(function)
|
||||
|
||||
Reduces the sequence down to a single value. `function` is a function that
|
||||
takes two arguments, the accumulator and sequence item and returns the new
|
||||
accumulator value. The accumulator is initialized from the first item in the
|
||||
sequence. Then, the function is invoked on each remaining item in the sequence,
|
||||
iteratively updating the accumulator.
|
||||
|
||||
It is a runtime error to call this on an empty sequence.
|
||||
|
||||
### **reduce**(seed, function)
|
||||
|
||||
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]
|
||||
|
||||
If the sequence is already a list, this creates a copy of it.
|
||||
|
||||
### **where**(predicate)
|
||||
|
||||
Creates a new sequence containing only the elements from the original sequence
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
sequence.
|
||||
|
||||
This means you can use `where(_)` for things like infinite sequences or
|
||||
sequences that have side effects when you iterate over them. But it also means
|
||||
that changes to the original sequence will be reflected in the filtered
|
||||
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]
|
||||
@ -1,258 +0,0 @@
|
||||
^title String Class
|
||||
|
||||
A string is an immutable array of bytes. Strings usually store text, in which
|
||||
case the bytes are the UTF-8 encoding of the text's code points. But you can put
|
||||
any kind of byte values in there you want, including null bytes or invalid
|
||||
UTF-8.
|
||||
|
||||
There are a few ways to think of a string:
|
||||
|
||||
* As a searchable chunk of text composed of a sequence of textual code points.
|
||||
|
||||
* As an iterable sequence of code point numbers.
|
||||
|
||||
* As a flat array of directly indexable bytes.
|
||||
|
||||
All of those are useful for some problems, so the string API supports all three.
|
||||
The first one is the most common, so that's what methods directly on the string
|
||||
class cater to.
|
||||
|
||||
In UTF-8, a single Unicode code point—very roughly a single
|
||||
"character"—may encode to one or more bytes. This means you can't
|
||||
efficiently index by code point. There's no way to jump directly to, say, the
|
||||
fifth code point in a string without walking the string from the beginning and
|
||||
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]
|
||||
|
||||
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
|
||||
|
||||
If you want to work with a string as a sequence numeric code points, call the
|
||||
`codePoints` getter. It returns a [Sequence](sequence.html) that decodes UTF-8
|
||||
and iterates over the code points, returning each as a number.
|
||||
|
||||
If you want to get at the raw bytes, call `bytes`. This returns a Sequence that
|
||||
ignores any UTF-8 encoding and works directly at the byte level.
|
||||
|
||||
## Static Methods
|
||||
|
||||
### String.**fromCodePoint**(codePoint)
|
||||
|
||||
Creates a new string containing the UTF-8 encoding of `codePoint`.
|
||||
|
||||
:::wren
|
||||
String.fromCodePoint(8225) //> ‡
|
||||
|
||||
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`.
|
||||
|
||||
:::wren
|
||||
String.fromByte(255) //> <20>
|
||||
|
||||
It is a runtime error if `byte` is not an integer between `0` and `0xff`, inclusive.
|
||||
|
||||
## Methods
|
||||
|
||||
### **bytes**
|
||||
|
||||
Gets a [`Sequence`](sequence.html) that can be used to access the raw bytes of
|
||||
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")
|
||||
|
||||
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
|
||||
the string, and runs in constant time instead.
|
||||
|
||||
### **codePoints**
|
||||
|
||||
Gets a [`Sequence`](sequence.html) that can be used to access the UTF-8 decode
|
||||
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 "ᴥ")
|
||||
|
||||
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 "ᵔ")
|
||||
|
||||
### **contains**(other)
|
||||
|
||||
Checks if `other` is a substring of the string.
|
||||
|
||||
It is a runtime error if `other` is not a string.
|
||||
|
||||
### **count**
|
||||
|
||||
Returns the number of code points in the string. Since UTF-8 is a
|
||||
variable-length encoding, this requires iterating over the entire string, which
|
||||
is relatively slow.
|
||||
|
||||
If the string contains bytes that are invalid UTF-8, each byte adds one to the
|
||||
count as well.
|
||||
|
||||
### **endsWith**(suffix)
|
||||
|
||||
Checks if the string ends with `suffix`.
|
||||
|
||||
It is a runtime error if `suffix` is not a string.
|
||||
|
||||
### **indexOf**(search)
|
||||
|
||||
Returns the index of the first byte matching `search` in the string or `-1` if
|
||||
`search` was not found.
|
||||
|
||||
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 a byte offset `start`. The start can be
|
||||
negative to count backwards from 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][] for iterating over the *code points* in the
|
||||
string:
|
||||
|
||||
[iterator protocol]: ../../control-flow.html#the-iterator-protocol
|
||||
|
||||
:::wren
|
||||
var codePoints = []
|
||||
for (c in "(ᵔᴥᵔ)") {
|
||||
codePoints.add(c)
|
||||
}
|
||||
|
||||
System.print(codePoints) //> [(, ᵔ, ᴥ, ᵔ, )]
|
||||
|
||||
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`.
|
||||
|
||||
:::wren
|
||||
var string = "abc abc abc"
|
||||
System.print(string.replace(" ", "")) //> abcabcabc
|
||||
|
||||
### **split**(separator)
|
||||
|
||||
Returns a list of one or more strings separated by `separator`.
|
||||
|
||||
:::wren
|
||||
var string = "abc abc abc"
|
||||
System.print(string.split(" ")) //> [abc, abc, abc]
|
||||
|
||||
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.
|
||||
|
||||
:::wren
|
||||
System.print(" \nstuff\r\t".trim()) //> stuff
|
||||
|
||||
### **trim**(chars)
|
||||
|
||||
Returns a new string with all code points in `chars` removed from the beginning
|
||||
and end of this string.
|
||||
|
||||
:::wren
|
||||
System.print("ᵔᴥᵔᴥᵔbearᵔᴥᴥᵔᵔ".trim("ᵔᴥ")) //> bear
|
||||
|
||||
### **trimEnd**()
|
||||
|
||||
Like `trim()` but only removes from the end of the string.
|
||||
|
||||
:::wren
|
||||
System.print(" \nstuff\r\t".trimEnd()) //> " \nstuff"
|
||||
|
||||
### **trimEnd**(chars)
|
||||
|
||||
Like `trim()` but only removes from the end of the string.
|
||||
|
||||
:::wren
|
||||
System.print("ᵔᴥᵔᴥᵔbearᵔᴥᴥᵔᵔ".trimEnd("ᵔᴥ")) //> ᵔᴥᵔᴥᵔbear
|
||||
|
||||
### **trimStart**()
|
||||
|
||||
Like `trim()` but only removes from the beginning of the string.
|
||||
|
||||
:::wren
|
||||
System.print(" \nstuff\r\t".trimStart()) //> "stuff\r\t"
|
||||
|
||||
### **trimStart**(chars)
|
||||
|
||||
Like `trim()` but only removes from the beginning of the string.
|
||||
|
||||
:::wren
|
||||
System.print("ᵔᴥᵔᴥᵔbearᵔᴥᴥᵔᵔ".trimStart("ᵔᴥ")) //> bearᵔᴥᴥᵔᵔ
|
||||
|
||||
### **+**(other) operator
|
||||
|
||||
Returns a new string that concatenates this string and `other`.
|
||||
|
||||
It is a runtime error if `other` is not a string.
|
||||
|
||||
### **==**(other) operator
|
||||
|
||||
Checks if the string is equal to `other`.
|
||||
|
||||
### **!=**(other) operator
|
||||
|
||||
Check if the string is not equal to `other`.
|
||||
|
||||
### **[**index**]** operator
|
||||
|
||||
Returns a string containing the code point starting at byte `index`.
|
||||
|
||||
:::wren
|
||||
System.print("ʕ•ᴥ•ʔ"[5]) //> ᴥ
|
||||
|
||||
Since `ʕ` is two bytes in UTF-8 and `•` is three, the fifth byte points to the
|
||||
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])
|
||||
|
||||
It is a runtime error if `index` is greater than the number of bytes in the
|
||||
string.
|
||||
@ -1,47 +0,0 @@
|
||||
^title System Class
|
||||
|
||||
The System class is a grab-bag of functionality exposed by the VM, mostly for
|
||||
use during development or debugging.
|
||||
|
||||
## Static Methods
|
||||
|
||||
### System.**clock**
|
||||
|
||||
Returns the number of seconds (including fractional seconds) since the program
|
||||
was started. This is usually used for benchmarking.
|
||||
|
||||
### System.**gc**()
|
||||
|
||||
Requests that the VM perform an immediate garbage collection to free unused
|
||||
memory.
|
||||
|
||||
### System.**print**()
|
||||
|
||||
Prints a single newline to the console.
|
||||
|
||||
### System.**print**(object)
|
||||
|
||||
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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
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.
|
||||
@ -1,99 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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="../">Modules</a></li>
|
||||
<li><a href="./">core</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>core classes</h2>
|
||||
<ul>
|
||||
<li><a href="bool.html">Bool</a></li>
|
||||
<li><a href="class.html">Class</a></li>
|
||||
<li><a href="fiber.html">Fiber</a></li>
|
||||
<li><a href="fn.html">Fn</a></li>
|
||||
<li><a href="list.html">List</a></li>
|
||||
<li><a href="map.html">Map</a></li>
|
||||
<li><a href="null.html">Null</a></li>
|
||||
<li><a href="num.html">Num</a></li>
|
||||
<li><a href="object.html">Object</a></li>
|
||||
<li><a href="range.html">Range</a></li>
|
||||
<li><a href="sequence.html">Sequence</a></li>
|
||||
<li><a href="string.html">String</a></li>
|
||||
<li><a href="system.html">System</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Modules</a></td>
|
||||
<td><a href="./">core</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>core classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="bool.html">Bool</a></li>
|
||||
<li><a href="class.html">Class</a></li>
|
||||
<li><a href="fiber.html">Fiber</a></li>
|
||||
<li><a href="fn.html">Fn</a></li>
|
||||
<li><a href="list.html">List</a></li>
|
||||
<li><a href="map.html">Map</a></li>
|
||||
<li><a href="null.html">Null</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="num.html">Num</a></li>
|
||||
<li><a href="object.html">Object</a></li>
|
||||
<li><a href="range.html">Range</a></li>
|
||||
<li><a href="sequence.html">Sequence</a></li>
|
||||
<li><a href="string.html">String</a></li>
|
||||
<li><a href="system.html">System</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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,49 +0,0 @@
|
||||
^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)
|
||||
* [os](os)
|
||||
* [scheduler](scheduler)
|
||||
* [timer](timer)
|
||||
@ -1,15 +0,0 @@
|
||||
^title Directory Class
|
||||
|
||||
A directory on the file system.
|
||||
|
||||
## Static Methods
|
||||
|
||||
### Directory.**exists**(path)
|
||||
|
||||
Whether a directory exists at `path`. This returns `false` for files or other
|
||||
special file system entities.
|
||||
|
||||
### Directory.**list**(path)
|
||||
|
||||
Lists the contents of the directory at `path`. Returns a sorted list of path
|
||||
strings for all of the contents of the directory.
|
||||
@ -1,48 +0,0 @@
|
||||
^title FileFlags Class
|
||||
|
||||
Contains constants for the various file flags used to open or create a file.
|
||||
These correspond directly to the flags that can be passed to the POSIX
|
||||
[`open()`][open] syscall.
|
||||
|
||||
[open]: http://linux.die.net/man/2/open
|
||||
|
||||
They are integers and can be bitwise or'ed together to produce a composite
|
||||
flag.
|
||||
|
||||
## Static Methods
|
||||
|
||||
### FileFlags.**readOnly**
|
||||
|
||||
The file can be read from but not written. Equivalent to `O_RDONLY`.
|
||||
|
||||
### FileFlags.**writeOnly**
|
||||
|
||||
The file can be written but not read from. Equivalent to `O_WRONLY`.
|
||||
|
||||
### FileFlags.**readWrite**
|
||||
|
||||
The file can be both read from and written to. Equivalent to `O_RDWR`.
|
||||
|
||||
### FileFlags.**sync**
|
||||
|
||||
Writes will block until the data has been physically written to the underling
|
||||
hardware. This does *not* affect whether or the file API is synchronous. File
|
||||
operations are always asynchronous in Wren and may allow other scheduled fibers
|
||||
to run.
|
||||
|
||||
This is a lower-level flag that ensures that when a write completes, it has
|
||||
been flushed all the way to disc.
|
||||
|
||||
### FileFlags.**create**
|
||||
|
||||
Creates a new file if a file at the given path does not already exist.
|
||||
|
||||
### FileFlags.**truncate**
|
||||
|
||||
If the file already exists and can be written to, its previous contents are
|
||||
discarded.
|
||||
|
||||
### FileFlags.**exclusive**
|
||||
|
||||
Ensures that a new file must be created. If a file already exists at the given
|
||||
path, this flag will cause the operation to fail.
|
||||
@ -1,131 +0,0 @@
|
||||
^title File Class
|
||||
|
||||
Lets you work with files on the file system. An instance of this class
|
||||
represents an open file with a file descriptor.
|
||||
|
||||
When you are done with a file object, it's a good idea to explicitly close it.
|
||||
If you don't, the GC will close it when the file is no longer used and gets
|
||||
finalized, but that may take a while. In the meantime, leaving it open wastes
|
||||
a file descriptor.
|
||||
|
||||
## Static Methods
|
||||
|
||||
### File.**create**(path, fn)
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
### File.**delete**(path)
|
||||
|
||||
Deletes the file at `path`.
|
||||
|
||||
### File.**exists**(path)
|
||||
|
||||
Whether a regular file exists at `path`. This returns `false` for directories
|
||||
or other special file system entities.
|
||||
|
||||
### File.**open**(path, fn)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
### File.**read**(path)
|
||||
|
||||
Reads the entire contents of the file at `path` and returns it as a string.
|
||||
|
||||
:::wren
|
||||
File.read("words.txt")
|
||||
|
||||
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
|
||||
whatever encoding the file uses.
|
||||
|
||||
### File.**realPath**(path)
|
||||
|
||||
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
|
||||
|
||||
### File.**size**(path)
|
||||
|
||||
Returns the size in bytes of the contents of the file at `path`.
|
||||
|
||||
## Constructors
|
||||
|
||||
### File.**create**(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()
|
||||
|
||||
### File.**open**(path)
|
||||
|
||||
Opens the file at `path` for reading. You are responsible for closing it when
|
||||
done with it.
|
||||
|
||||
## Methods
|
||||
|
||||
### **descriptor**
|
||||
|
||||
The numeric file descriptor used to access the file.
|
||||
|
||||
### **isOpen**
|
||||
|
||||
Whether the file is still open or has been closed.
|
||||
|
||||
### **size**
|
||||
|
||||
The size of the contents of the file in bytes.
|
||||
|
||||
### **close**()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
### **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
|
||||
}
|
||||
|
||||
### **writeBytes**(bytes)
|
||||
|
||||
Writes the raw bytes of the string `bytes` to the end of the file.
|
||||
|
||||
### **writeBytes**(bytes, offset)
|
||||
|
||||
Writes the raw bytes of the string `bytes` to the to the file, starting at
|
||||
`offset`. Any overlapping bytes already in the file at the offset are
|
||||
overwritten.
|
||||
@ -1,9 +0,0 @@
|
||||
^title Module "io"
|
||||
|
||||
Provides access to operating system streams and the file system.
|
||||
|
||||
* [Directory](directory.html)
|
||||
* [File](file.html)
|
||||
* [Stat](stat.html)
|
||||
* [Stdin](stdin.html)
|
||||
* [Stdout](stdout.html)
|
||||
@ -1,63 +0,0 @@
|
||||
^title Stat Class
|
||||
|
||||
A data structure describing the low-level details of a file system entry.
|
||||
|
||||
## Static Methods
|
||||
|
||||
### Stat.**path**(path)
|
||||
|
||||
"Stats" the file or directory at `path`.
|
||||
|
||||
## Methods
|
||||
|
||||
### **blockCount**
|
||||
|
||||
The number of system blocks allocated on disk for the file.
|
||||
|
||||
### **blockSize**
|
||||
|
||||
The preferred block size in bytes for interacting with the file. It may vary
|
||||
from file to file.
|
||||
|
||||
### **device**
|
||||
|
||||
The ID of the device containing the entry.
|
||||
|
||||
### **group**
|
||||
|
||||
Numeric group ID of the file's owner.
|
||||
|
||||
### **inode**
|
||||
|
||||
The [inode][] number of the entry.
|
||||
|
||||
[inode]: https://en.wikipedia.org/wiki/Inode
|
||||
|
||||
### **isDirectory**
|
||||
|
||||
Whether the file system entity is a directory.
|
||||
|
||||
### **isFile**
|
||||
|
||||
Whether the file system entity is a regular file, as opposed to a directory or
|
||||
other special entity.
|
||||
|
||||
### **linkCount**
|
||||
|
||||
The number of hard links to the entry.
|
||||
|
||||
### **mode**
|
||||
|
||||
A bit field describing the entry's type and protection flags.
|
||||
|
||||
### **size**
|
||||
|
||||
The size of the entry in bytes.
|
||||
|
||||
### **specialDevice**
|
||||
|
||||
The device ID for the entry, if it's a special file.
|
||||
|
||||
### **user**
|
||||
|
||||
Numeric user ID of the file's owner.
|
||||
@ -1,45 +0,0 @@
|
||||
^title Stdin Class
|
||||
|
||||
The standard input stream.
|
||||
|
||||
## Static Methods
|
||||
|
||||
### **isRaw**
|
||||
|
||||
Returns `true` if stdin is in raw mode. When in raw mode, input is not echoed
|
||||
or buffered, and all characters, even non-printing and control characters go
|
||||
into stdin.
|
||||
|
||||
Defaults to `false`.
|
||||
|
||||
### **isRaw**=(value)
|
||||
|
||||
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
|
||||
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.
|
||||
@ -1,11 +0,0 @@
|
||||
^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.
|
||||
@ -1,85 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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="../">Modules</a></li>
|
||||
<li><a href="./">io</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>io classes</h2>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>io classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<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>
|
||||
</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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,5 +0,0 @@
|
||||
^title Module "meta"
|
||||
|
||||
**TODO**
|
||||
|
||||
* [Meta](meta.html)
|
||||
@ -1,7 +0,0 @@
|
||||
^title Meta Class
|
||||
|
||||
**TODO**
|
||||
|
||||
## Methods
|
||||
|
||||
**TODO**
|
||||
@ -1,75 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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="../">Modules</a></li>
|
||||
<li><a href="./">meta</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>meta classes</h2>
|
||||
<ul>
|
||||
<li><a href="meta.html">Meta</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Modules</a></td>
|
||||
<td><a href="./">meta</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>meta classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="meta.html">Meta</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,7 +0,0 @@
|
||||
^title Module "os"
|
||||
|
||||
The os module exposes classes for accessing capabilities provided by the
|
||||
underlying operating system.
|
||||
|
||||
* [Platform](platform.html)
|
||||
* [Process](process.html)
|
||||
@ -1,30 +0,0 @@
|
||||
^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.
|
||||
@ -1,38 +0,0 @@
|
||||
^title Process Class
|
||||
|
||||
The Process class lets you work with operating system processes, including the
|
||||
currently running one.
|
||||
|
||||
## Static Methods
|
||||
|
||||
### **allArguments**
|
||||
|
||||
The list of command-line arguments that were passed when the Wren process was
|
||||
spawned. This includes the Wren executable itself, the path to the file being
|
||||
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"]
|
||||
|
||||
### **arguments**
|
||||
|
||||
The list of command-line arguments that were passed to your program when the
|
||||
Wren process was spawned. This does not include arguments handled by Wren
|
||||
itself.
|
||||
|
||||
If you run:
|
||||
|
||||
:::bash
|
||||
$ wren file.wren arg
|
||||
|
||||
This returns:
|
||||
|
||||
:::wren
|
||||
System.print(Process.arguments) //> ["arg"]
|
||||
@ -1,77 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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="../">Modules</a></li>
|
||||
<li><a href="./">os</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>os classes</h2>
|
||||
<ul>
|
||||
<li><a href="platform.html">Platform</a></li>
|
||||
<li><a href="process.html">Process</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Modules</a></td>
|
||||
<td><a href="./">os</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
<td>
|
||||
<ul>
|
||||
</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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,10 +0,0 @@
|
||||
^title Module "random"
|
||||
|
||||
This module provides a simple, fast pseudo-random number generator.
|
||||
|
||||
It is an optional module. You can omit it from your application by setting the
|
||||
preprocessor constant `WREN_OPT_RANDOM` to `0`.
|
||||
|
||||
It contains a single class:
|
||||
|
||||
* [Random](random.html)
|
||||
@ -1,130 +0,0 @@
|
||||
^title Random Class
|
||||
|
||||
A simple, fast pseudo-random number generator. Internally, it uses the [well
|
||||
equidistributed long-period linear PRNG][well] (WELL512a).
|
||||
|
||||
[well]: https://en.wikipedia.org/wiki/Well_equidistributed_long-period_linear
|
||||
|
||||
Each instance of the class generates a sequence of randomly distributed numbers
|
||||
based on the internal state of the object. The state is initialized from a
|
||||
*seed*. Two instances with the same seed generate the exact same sequence of
|
||||
numbers.
|
||||
|
||||
|
||||
It must be imported from the [random][] module:
|
||||
|
||||
:::wren
|
||||
import "random" for Random
|
||||
|
||||
[random]: ../
|
||||
|
||||
## Constructors
|
||||
|
||||
### Random.**new**()
|
||||
|
||||
Creates a new generator whose state is seeded based on the current time.
|
||||
|
||||
:::wren
|
||||
var random = Random.new()
|
||||
|
||||
### Random.**new**(seed)
|
||||
|
||||
Creates a new generator initialized with [seed]. The seed can either be a
|
||||
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)
|
||||
|
||||
## Methods
|
||||
|
||||
### **float**()
|
||||
|
||||
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
|
||||
|
||||
### **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
|
||||
|
||||
### **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
|
||||
|
||||
### **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
|
||||
|
||||
### **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
|
||||
|
||||
### **sample**(list)
|
||||
|
||||
Selects a random element from `list`.
|
||||
|
||||
### **sample**(list, count)
|
||||
|
||||
Samples `count` randomly chosen unique elements from `list`.
|
||||
|
||||
This uses "random without replacement" sampling—no index in the list will
|
||||
be selected more than once.
|
||||
|
||||
Returns a new list of the selected elements.
|
||||
|
||||
It is an error if `count` is greater than the number of elements in the list.
|
||||
|
||||
### **shuffle**(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]
|
||||
|
||||
Uses the Fisher-Yates algorithm to ensure that all permutations are chosen
|
||||
with equal probability.
|
||||
|
||||
Keep in mind that a list with even a modestly large number of elements has an
|
||||
astronomically large number of permutations. For example, there are about 10^74
|
||||
ways a deck of 56 cards can be shuffled. The random number generator's internal
|
||||
state is not that large, which means there are many permutations it will never
|
||||
generate.
|
||||
@ -1,75 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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="../">Modules</a></li>
|
||||
<li><a href="./">random</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>random classes</h2>
|
||||
<ul>
|
||||
<li><a href="random.html">Random</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Modules</a></td>
|
||||
<td><a href="./">random</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>random classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="random.html">Random</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,5 +0,0 @@
|
||||
^title Module "scheduler"
|
||||
|
||||
**TODO**
|
||||
|
||||
* [Scheduler](scheduler.html)
|
||||
@ -1,7 +0,0 @@
|
||||
^title Scheduler Class
|
||||
|
||||
**TODO**
|
||||
|
||||
## Methods
|
||||
|
||||
**TODO**
|
||||
@ -1,75 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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="../">Modules</a></li>
|
||||
<li><a href="./">scheduler</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>scheduler classes</h2>
|
||||
<ul>
|
||||
<li><a href="scheduler.html">Scheduler</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Modules</a></td>
|
||||
<td><a href="./">scheduler</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>scheduler classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="scheduler.html">Scheduler</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,95 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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>
|
||||
<section>
|
||||
<h2>core</h2>
|
||||
<ul>
|
||||
<li><a href="core">core</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>optional</h2>
|
||||
<ul>
|
||||
<li><a href="meta">meta</a></li>
|
||||
<li><a href="random">random</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>cli</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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,5 +0,0 @@
|
||||
^title Module "timer"
|
||||
|
||||
**TODO**
|
||||
|
||||
* [Timer](timer.html)
|
||||
@ -1,75 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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="../">Modules</a></li>
|
||||
<li><a href="./">timer</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>timer classes</h2>
|
||||
<ul>
|
||||
<li><a href="timer.html">Timer</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Modules</a></td>
|
||||
<td><a href="./">timer</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>timer classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="timer.html">Timer</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,7 +0,0 @@
|
||||
^title Timer Class
|
||||
|
||||
**TODO**
|
||||
|
||||
## Methods
|
||||
|
||||
**TODO**
|
||||
@ -1,249 +0,0 @@
|
||||
^title Performance
|
||||
|
||||
Even though most benchmarks aren't worth the pixels they're printed on, people
|
||||
seem to like them, so here's a few:
|
||||
|
||||
<h3>Method Call</h3>
|
||||
<table class="chart">
|
||||
<tr>
|
||||
<th>wren</th><td><div class="chart-bar wren" style="width: 14%;">0.12s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>luajit (-joff)</th><td><div class="chart-bar" style="width: 18%;">0.16s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ruby</th><td><div class="chart-bar" style="width: 23%;">0.20s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>lua</th><td><div class="chart-bar" style="width: 41%;">0.35s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python3</th><td><div class="chart-bar" style="width: 91%;">0.78s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python</th><td><div class="chart-bar" style="width: 100%;">0.85s </div></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3>DeltaBlue</h3>
|
||||
<table class="chart">
|
||||
<tr>
|
||||
<th>wren</th><td><div class="chart-bar wren" style="width: 22%;">0.13s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python3</th><td><div class="chart-bar" style="width: 83%;">0.48s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python</th><td><div class="chart-bar" style="width: 100%;">0.57s </div></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3>Binary Trees</h3>
|
||||
<table class="chart">
|
||||
<tr>
|
||||
<th>luajit (-joff)</th><td><div class="chart-bar" style="width: 20%;">0.11s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>wren</th><td><div class="chart-bar wren" style="width: 41%;">0.22s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ruby</th><td><div class="chart-bar" style="width: 46%;">0.24s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python</th><td><div class="chart-bar" style="width: 71%;">0.37s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python3</th><td><div class="chart-bar" style="width: 73%;">0.38s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>lua</th><td><div class="chart-bar" style="width: 100%;">0.52s </div></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3>Recursive Fibonacci</h3>
|
||||
<table class="chart">
|
||||
<tr>
|
||||
<th>luajit (-joff)</th><td><div class="chart-bar" style="width: 17%;">0.10s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>wren</th><td><div class="chart-bar wren" style="width: 35%;">0.20s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ruby</th><td><div class="chart-bar" style="width: 39%;">0.22s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>lua</th><td><div class="chart-bar" style="width: 49%;">0.28s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python</th><td><div class="chart-bar" style="width: 90%;">0.51s </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python3</th><td><div class="chart-bar" style="width: 100%;">0.57s </div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**Shorter bars are better.** Each benchmark is run ten times and the best time
|
||||
is kept. It only measures the time taken to execute the benchmarked code
|
||||
itself, not interpreter startup.
|
||||
|
||||
These were run on my MacBook Pro 2.3 GHz Intel Core i7 with 16 GB of 1,600 MHz
|
||||
DDR3 RAM. Tested against Lua 5.2.3, LuaJIT 2.0.2, Python 2.7.5, Python 3.3.4,
|
||||
ruby 2.0.0p247. LuaJIT is run with the JIT *disabled* (i.e. in bytecode
|
||||
interpreter mode) since I want to support platforms where JIT-compilation is
|
||||
disallowed. LuaJIT with the JIT enabled is *much* faster than all of the other
|
||||
languages benchmarked, including Wren, because Mike Pall is a robot from the
|
||||
future.
|
||||
|
||||
The benchmark harness and programs are
|
||||
[here](https://github.com/wren-lang/wren/tree/master/test/benchmark).
|
||||
|
||||
## Why is Wren fast?
|
||||
|
||||
Languages come in four rough performance buckets, from slowest to fastest:
|
||||
|
||||
1. Tree-walk interpreters: Ruby 1.8.7 and earlier, Io, that
|
||||
interpreter you wrote for a class in college.
|
||||
|
||||
2. Bytecode interpreters: CPython,
|
||||
Ruby 1.9 and later, Lua, early JavaScript VMs.
|
||||
|
||||
3. JIT compiled dynamically typed languages: Modern JavaScript VMs,
|
||||
LuaJIT, PyPy, some Lisp/Scheme implementations.
|
||||
|
||||
4. Statically typed languages: C, C++, Java, C#, Haskell, etc.
|
||||
|
||||
Most languages in the first bucket aren't suitable for production use. (Servers
|
||||
are one exception, because you can throw more hardware at a slow language
|
||||
there.) Languages in the second bucket are fast enough for many use cases, even
|
||||
on client hardware, as the success of the listed languages shows. Languages in
|
||||
the third bucket are quite fast, but their implementations are breathtakingly
|
||||
complex, often rivaling that of compilers for statically-typed languages.
|
||||
|
||||
Wren is in the second bucket. If you want a simple implementation that's fast
|
||||
enough for real use, this is the sweet spot. In addition, Wren has a few tricks
|
||||
up its sleeve:
|
||||
|
||||
### A compact value representation
|
||||
|
||||
A core piece of a dynamic language implementation is the data structure used
|
||||
for variables. It needs to be able to store (or reference) a value of any type,
|
||||
while also being as compact as possible. Wren uses a technique called *[NaN
|
||||
tagging][]* for this.
|
||||
|
||||
[nan tagging]: http://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations
|
||||
|
||||
All values are stored internally in Wren as small, eight-byte double-precision
|
||||
floats. Since that is also Wren's number type, in order to do arithmetic, no
|
||||
conversion is needed before the "raw" number can be accessed: a value holding a
|
||||
number *is* a valid double. This keeps arithmetic fast.
|
||||
|
||||
To store values of other types, it turns out there's a ton of unused bits in a
|
||||
NaN double. You can stuff a pointer for heap-allocated objects, with room left
|
||||
over for special values like `true`, `false`, and `null`. This means numbers,
|
||||
bools, and null are unboxed. It also means an entire value is only eight bytes,
|
||||
the native word size on 64-bit machines. Smaller = faster when you take into
|
||||
account CPU caching and the cost of passing values around.
|
||||
|
||||
### Fixed object layout
|
||||
|
||||
Most dynamic languages treat objects as loose bags of named properties. You can
|
||||
freely add and remove properties from an object after you've created it.
|
||||
Languages like Lua and JavaScript don't even have a well-defined concept of a
|
||||
"type" of object.
|
||||
|
||||
Wren is strictly class-based. Every object is an instance of a class. Classes
|
||||
in turn have a well-defined declarative syntax, and cannot be imperatively
|
||||
modified. In addition, fields in Wren are private to the class—they can
|
||||
only be accessed from methods defined directly on that class.
|
||||
|
||||
Put all of that together and it means you can determine at *compile* time
|
||||
exactly how many fields an object has and what they are. In other languages,
|
||||
when you create an object, you allocate some initial memory for it, but that
|
||||
may have to be reallocated multiple times as fields are added and the object
|
||||
grows. Wren just does a single allocation up front for exactly the right number
|
||||
of fields.
|
||||
|
||||
Likewise, when you access a field in other languages, the interpreter has to
|
||||
look it up by name in a hash table in the object, and then maybe walk its
|
||||
inheritance chain if it can't find it. It must do this every time since fields
|
||||
may be added freely. In Wren, field access is just accessing a slot in the
|
||||
instance by an offset known at compile time: it's just adding a few pointers.
|
||||
|
||||
### Copy-down inheritance
|
||||
|
||||
When you call a method on an object, the method must be located. It could be
|
||||
defined directly on the object's class, or it may be inheriting it from some
|
||||
superclass. This means that in the worst case, you may have to walk the
|
||||
inheritance chain to find it.
|
||||
|
||||
Advanced implementations do very smart things to optimize this, but it's made
|
||||
more difficult by the mutable nature of the underlying language: if you can add
|
||||
new methods to existing classes freely or change the inheritance hierarchy, the
|
||||
lookup for a given method may actually change over time. You have to check for
|
||||
that which costs CPU cycles.
|
||||
|
||||
Wren's inheritance hierarchy is static and fixed at class definition time. This
|
||||
means that we can copy down all inherited methods in the subclass when it's
|
||||
created since we know those will never change. Method dispatch then just
|
||||
requires locating the method in the class of the receiver.
|
||||
|
||||
### Method signatures
|
||||
|
||||
Wren supports overloading by arity using its concept of [signatures]. This makes
|
||||
the language more expressive, but also faster. When a method is called, we look
|
||||
it up on the receiver's class. If we succeed in finding it, we also know it has
|
||||
the right number of parameters.
|
||||
|
||||
This lets Wren avoid the extra checking most languages need to do at runtime to
|
||||
handle too few or too many arguments being passed to a method. In Wren, it's not
|
||||
*syntactically* possible to call a method with the wrong number of arguments.
|
||||
|
||||
[signatures]: method-calls.html#signature
|
||||
|
||||
### Computed gotos
|
||||
|
||||
On compilers that support it, Wren's core bytecode interpreter loop uses
|
||||
something called [*computed gotos*][goto]. The hot core of a bytecode
|
||||
interpreter is effectively a giant `switch` on the instruction being executed.
|
||||
|
||||
[goto]: http://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables/
|
||||
|
||||
Doing that using an actual `switch` confounds the CPU's [branch
|
||||
predictor][]—there is basically a single branch point for the entire
|
||||
interpreter. That quickly saturates the predictor and it just gets confused and
|
||||
fails to predict anything, which leads to more CPU stalls and pipeline flushes.
|
||||
|
||||
[branch predictor]: http://en.wikipedia.org/wiki/Branch_predictor
|
||||
|
||||
Using computed gotos gives you a separate branch point at the end of each
|
||||
instruction. Each gets its own branch prediction, which often succeeds since
|
||||
some instruction pairs are more common than others. In my rough testing, this
|
||||
makes a 5-10% performance difference.
|
||||
|
||||
### A single-pass compiler
|
||||
|
||||
Compile time is a relatively small component of a language's performance: code
|
||||
only has to be compiled once but a given line of code may be run many times.
|
||||
However, fast compilation helps with *startup* speed—the time it takes to
|
||||
get anything up and running. For that, Wren's compiler is quite fast.
|
||||
|
||||
It's modeled after Lua's compiler. Instead of tokenizing and then parsing to
|
||||
create a bunch of AST structures which are then consumed and deallocated by
|
||||
later phases, it emits code directly during parsing. This means it does minimal
|
||||
memory allocation during a parse and has very little overhead.
|
||||
|
||||
## Why don't other languages do this?
|
||||
|
||||
Most of Wren's performance comes from language design decisions. While it's
|
||||
dynamically *typed* and *dispatched*, classes are relatively statically
|
||||
*defined*. That makes a lot of things much easier. Other languages have a much
|
||||
more mutable object model, and cannot change that without breaking lots of
|
||||
existing code.
|
||||
|
||||
Wren's closest sibling, by far, is Lua. Lua is more dynamic than Wren which
|
||||
makes its job harder. Lua also tries very hard to be compatible across a wide
|
||||
range of hardware and compilers. If you have a C89 compiler for it, odds are
|
||||
very good that you can run Lua on it.
|
||||
|
||||
Wren cares about compatibility, but it requires C99 or C++98 and IEEE double
|
||||
precision floats. That may exclude some edge case hardware, but makes things
|
||||
like NaN tagging, computed gotos, and some other tricks possible.
|
||||
|
||||
<script src="script.js"></script>
|
||||
@ -1,172 +0,0 @@
|
||||
^title Q & A
|
||||
|
||||
## Why did you create Wren?
|
||||
|
||||
Other creative endeavors aren't immediately met with existential crises, but
|
||||
for some reason programmers don't seem to like new languages. Here's the niche
|
||||
I'm trying to fill:
|
||||
|
||||
There are a few scripting languages used for embedding in applications. Lua is
|
||||
the main one. TCL used to be. There's also Guile, increasingly JavaScript, and
|
||||
some applications embed Python. I'm an ex-game developer, so when I think
|
||||
"scripting", I tend to think "game scripting".
|
||||
|
||||
Lua is nice: it's small, simple, and fast. But—and I don't mean this as a
|
||||
criticism—it's also weird if you're used to languages like C++ and Java.
|
||||
The syntax is different. The semantics, especially the object model are
|
||||
unusual. Anyone can get used to 1-based indexing, but things like metatables
|
||||
really show that objects were bolted onto Lua after the fact.
|
||||
|
||||
I think there's room for a language as simple as Lua, but that feels natural to
|
||||
someone with an OOP background. Wren is my attempt at that.
|
||||
|
||||
## Why classes?
|
||||
|
||||
Thanks to JavaScript's popularity, lots of people are discovering prototypes
|
||||
right now, and the paradigm is experiencing a popularity boom. I think
|
||||
prototypes are interesting, but after [several years playing with them][finch],
|
||||
I concluded (like many people on the original Self project that invented
|
||||
prototypes) that classes are more usable.
|
||||
|
||||
[finch]: http://finch.stuffwithstuff.com/
|
||||
|
||||
Here's an example of that kind of object-oriented programming in Lua:
|
||||
|
||||
:::lua
|
||||
Account = {}
|
||||
Account.__index = Account
|
||||
|
||||
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
|
||||
|
||||
function Account:withdraw(amount)
|
||||
self.balance = self.balance - amount
|
||||
end
|
||||
|
||||
-- create and use an Account
|
||||
account = Account.create(1000)
|
||||
account:withdraw(100)
|
||||
|
||||
Here's the same example in Wren:
|
||||
|
||||
:::wren
|
||||
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)
|
||||
|
||||
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
|
||||
to show with Wren that it is those languages that are complex, and not classes
|
||||
themselves.
|
||||
|
||||
Smalltalk, the language that inspired most of those languages, is famously
|
||||
simple. Its syntax [fits on an index card][card]. My aim is to keep Wren that
|
||||
minimal while still having the expressive power of [classes](classes.html).
|
||||
|
||||
[card]: http://www.jarober.com/blog/blogView?showComments=true&title=Readability+is+Key&entry=3506312690
|
||||
|
||||
## Why compile to bytecode?
|
||||
|
||||
The [performance page](performance.html) has more details, but the short answer
|
||||
is that bytecode is a nice trade-off between performance and simplicity. Also:
|
||||
|
||||
* Many devices like iPhones and game consoles don't allow executing code
|
||||
generated at runtime, which rules out just-in-time compilation.
|
||||
|
||||
* I think [fibers][] are a really powerful tool, and implementing them is
|
||||
straightforward in a bytecode VM that doesn't use the native stack.
|
||||
|
||||
[fibers]: concurrency.html
|
||||
|
||||
## Why is the VM stack-based instead of register-based?
|
||||
|
||||
Bytecode VMs come in two flavors. Stack-based VMs have short (usually one byte)
|
||||
instructions whose operands are implicitly understood to be at the top of the
|
||||
stack. That means you often have a couple of instructions to push some stuff on
|
||||
the stack and then an instruction to do something.
|
||||
|
||||
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
|
||||
|
||||
Can be a single bytecode instruction. In a stack-based language, it would be
|
||||
four—push `b`, push `c`, add, store `a`. (Though note that in both cases
|
||||
you've got 32 total bits of code.)
|
||||
|
||||
Lua used to be stack-based and switched to register-based and got a speed
|
||||
boost. Why not use registers for Wren?
|
||||
|
||||
I've implemented a [register-based VM
|
||||
before](http://finch.stuffwithstuff.com/). I think it's a cool model, but I
|
||||
don't think it would bring much benefit for Wren. It's more effort to compile,
|
||||
and I'm trying to keep Wren's implementation as simple as possible.
|
||||
|
||||
In return for that complexity, you can generate fewer instructions. However, I
|
||||
don't think Wren would be able to take advantage of that. Wren doesn't
|
||||
currently have any dedicated instructions for arithmetic. Operators are just
|
||||
regular method calls and can call user-defined procedures.
|
||||
|
||||
The calling convention for methods requires all of their parameters to be at
|
||||
the top of the caller's stack so that they can become bottom of the callee's
|
||||
stack frame window. To call `+` in Wren, we still have to push the arguments on
|
||||
top of the stack. Likewise, the method calling convention places the return
|
||||
value where the first argument was, so we'd have to move it back down to the
|
||||
destination slot after the call.
|
||||
|
||||
It may be worth having dedicated instructions for arithmetic that special case
|
||||
the built-in types before falling back to user-defined operator methods (which
|
||||
I assume is what Lua does since they added operator overloading late in the
|
||||
language's development). If that happens, it may be possible to switch to
|
||||
register-based.
|
||||
|
||||
But I'm not convinced it would be an actual performance win. A lot of details
|
||||
of the language affect whether a register-based VM is better. For example,
|
||||
assignments are statements in Lua but expressions in Wren, which would make
|
||||
them harder to compile to efficient register-based code.
|
||||
|
||||
## What about your other languages?
|
||||
|
||||
This is a strange question if you don't happen to know [who I am][me]. In the
|
||||
past, I've hacked on and blogged about a couple of other hobby languages like
|
||||
[Finch][] and [Magpie][].
|
||||
|
||||
[me]: http://journal.stuffwithstuff.com
|
||||
[magpie]: http://magpie-lang.org/
|
||||
|
||||
I started Finch to learn more about implementing an interpreter and also about
|
||||
the prototype paradigm. I learned a ton about both. Critically, I learned that
|
||||
I really prefer classes over prototypes. I started retrofitting classes into
|
||||
Finch but realized it was too big of a change, and thus Wren was born.
|
||||
|
||||
Wren is a replacement for Finch to me. I gave it a new name mainly so that I
|
||||
can keep Finch around in case other people want to take it and do something
|
||||
with it. I don't have any intention to work on it anymore.
|
||||
|
||||
Magpie is a trickier one. I really like the ideas behind Magpie. It's the
|
||||
general-purpose language I wish I had much of the time. I love pattern matching
|
||||
and multiple dispatch. I like how it integrates the event-based IO of [libuv][]
|
||||
with the simplicity of fibers.
|
||||
|
||||
[libuv]: https://github.com/joyent/libuv
|
||||
|
||||
But it's also a much more challenging project. As a general-purpose language,
|
||||
there's a ton of library work to do before Magpie is useful for anything. It
|
||||
has some unresolved GC issues. And I'm frankly not skilled enough right now to
|
||||
implement multiple dispatch efficiently.
|
||||
|
||||
Meanwhile, since I started working on Magpie, [Julia](http://julialang.org/)
|
||||
appeared and [Dylan](http://opendylan.org/) *re*appeared. I created Magpie
|
||||
partially to carry the torch of multiple dispatch, but others are starting to
|
||||
spread that light now.
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 5.3 KiB |
@ -1,563 +0,0 @@
|
||||
$header: "Sanchez", helvetica, arial, sans-serif;
|
||||
$subheader: "Lato", helvetica, arial, sans-serif;
|
||||
$body: "Source Sans Pro", georgia, serif;
|
||||
$code: "Source Code Pro", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
$header_h: 8em;
|
||||
$dark: hsl(210, 10%, 25%);
|
||||
$darker: hsl(210, 20%, 10%);
|
||||
$light: hsl(0, 0%, 100%);
|
||||
$gray-5: mix($dark, $light, 5%);
|
||||
$gray-10: mix($dark, $light, 10%);
|
||||
$gray-20: mix($dark, $light, 20%);
|
||||
$gray-30: mix($dark, $light, 30%);
|
||||
$gray-40: mix($dark, $light, 40%);
|
||||
$gray-50: mix($dark, $light, 50%);
|
||||
$gray-60: mix($dark, $light, 60%);
|
||||
$gray-80: mix($dark, $light, 80%);
|
||||
|
||||
$text: mix($light, #000, 20%);
|
||||
|
||||
$code-color: hsl(210, 20%, 30%);
|
||||
$code-bg: hsl(210, 0%, 99%);
|
||||
|
||||
$link: hsl(200, 60%, 50%);
|
||||
$link-hover: hsl(210, 100%, 80%);
|
||||
$link-dark: hsl(210, 60%, 20%);
|
||||
$link-glow: hsla(210, 100%, 50%, 0.4);
|
||||
|
||||
$module-link: hsl(150, 70%, 40%);
|
||||
$module-link-hover: hsl(130, 70%, 70%);
|
||||
$module-link-dark: hsl(160, 60%, 25%);
|
||||
$module-link-glow: hsla(130, 90%, 50%, 0.4);
|
||||
|
||||
* {
|
||||
-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: $light;
|
||||
color: $text;
|
||||
font: 16px/25px $body;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid $gray-5;
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: $gray-50;
|
||||
margin: 0;
|
||||
max-width: 24em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
date {
|
||||
color: $gray-80;
|
||||
}
|
||||
|
||||
.page {
|
||||
margin: 0 auto;
|
||||
width: 800px;
|
||||
min-height: calc(100% - 16.75em);
|
||||
|
||||
// Clear contents.
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
.main-column {
|
||||
position: relative;
|
||||
width: 560px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 7em;
|
||||
position: relative;
|
||||
margin: auto;
|
||||
display: block;
|
||||
left: -1em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
header {
|
||||
|
||||
.page {
|
||||
height: $header_h;
|
||||
}
|
||||
|
||||
background: $light;
|
||||
border-bottom: solid 1px $gray-10;
|
||||
|
||||
h1 {
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: ($header_h / 3) - 1.15em;
|
||||
padding: 0;
|
||||
font: 400 48px $header;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: $header_h + 0.5em;
|
||||
padding: 0;
|
||||
font: 500 13px $subheader;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
color: $gray-50;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $gray-80;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $link-hover;
|
||||
/*text-shadow: 0 0 6px $link-glow;*/
|
||||
}
|
||||
}
|
||||
|
||||
nav {
|
||||
float: right;
|
||||
width: 160px;
|
||||
margin-top: 2em;
|
||||
|
||||
h2 {
|
||||
color: $gray-80;
|
||||
font: 500 13px $subheader;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 6px 0 20px 0;
|
||||
}
|
||||
|
||||
li {
|
||||
font: 17px $body;
|
||||
color: $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: $gray-10;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
border-collapse: separate;
|
||||
border-spacing: 16px 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 16px 0 0 0;
|
||||
padding: 0 0 1px 0;
|
||||
border-bottom: solid 1px $gray-20;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-top: 30px;
|
||||
font: 500 36px/60px $header;
|
||||
color: $link;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font: 500 24px $header;
|
||||
margin: 24px 0 0 0;
|
||||
color: $link;
|
||||
|
||||
code {
|
||||
border: none;
|
||||
background: inherit;
|
||||
color: inherit;
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font: 20px $body;
|
||||
margin: 24px 0 0 0;
|
||||
color: $link;
|
||||
|
||||
code {
|
||||
border: none;
|
||||
background: inherit;
|
||||
color: inherit;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $link;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s, text-shadow 0.2s;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
main {
|
||||
@extend .main-column;
|
||||
margin-top: 2em;
|
||||
float: left;
|
||||
|
||||
.intro {
|
||||
border-bottom: solid 1px $gray-10;
|
||||
margin-bottom: -0.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
display: block;
|
||||
position: relative;
|
||||
max-width: 16em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $link-dark;
|
||||
}
|
||||
|
||||
.header-anchor {
|
||||
color: $light;
|
||||
}
|
||||
|
||||
h2:hover > .header-anchor,
|
||||
h3:hover > .header-anchor {
|
||||
color: $gray-10;
|
||||
}
|
||||
|
||||
h2:hover > .header-anchor:hover,
|
||||
h3:hover > .header-anchor:hover {
|
||||
color: $link-dark;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
p + p {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
code, pre {
|
||||
color: $code-color;
|
||||
font: 13px $code;
|
||||
background: $code-bg;
|
||||
border-radius: 2px;
|
||||
border: solid 1px hsl(200, 20%, 88%);
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 1px 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 10px 0;
|
||||
line-height: 20px;
|
||||
padding: 10px;
|
||||
|
||||
// Scroll horizontally if not wide enough.
|
||||
overflow: auto;
|
||||
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 4em;
|
||||
padding: 20px 0 40px 0;
|
||||
font: 14px $body;
|
||||
background: $dark;
|
||||
color: $gray-20;
|
||||
border-top: solid 1px $darker;
|
||||
text-align: center;
|
||||
|
||||
text-shadow: 0 1px 1px $darker;
|
||||
|
||||
a {
|
||||
color: $link-hover;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $link;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
// Syntax highlighting.
|
||||
.codehilite pre {
|
||||
// Comments.
|
||||
span.c1, span.cm { color: mix($code-color, $code-bg, 60%); }
|
||||
|
||||
// Keywords.
|
||||
span.k, span.kd, span.kc, span.kt, span.nb { color: hsl(200, 70%, 50%); }
|
||||
|
||||
// Names.
|
||||
span.vg { color: hsl(180, 70%, 35%); }
|
||||
span.vi { color: hsl(90, 80%, 35%); }
|
||||
span.vc { color: hsl(130, 60%, 40%); }
|
||||
|
||||
// C function name.
|
||||
span.nf { color: hsl(180, 60%, 40%); }
|
||||
|
||||
// Numbers.
|
||||
span.m, span.mi, span.mf { color: hsl(90, 40%, 50%); }
|
||||
|
||||
// Strings.
|
||||
span.s, span.s2 { color: hsl(40, 90%, 40%); }
|
||||
// Escape.
|
||||
span.se { color: hsl(50, 90%, 45%); }
|
||||
// Interpolation.
|
||||
span.si { color: hsl(50, 90%, 40%); background: hsl(40, 90%, 95%); }
|
||||
|
||||
// Operators and punctuation.
|
||||
span.o { color: hsl(200, 40%, 50%); }
|
||||
span.p { color: mix($code-color, $code-bg, 80%); }
|
||||
|
||||
// Preprocessor directives.
|
||||
span.cp { color: hsl(270, 40%, 60%); }
|
||||
|
||||
span.output {
|
||||
float: right;
|
||||
color: hsl(150, 50%, 45%);
|
||||
|
||||
&::before, &::after {
|
||||
color: hsl(150, 50%, 80%);
|
||||
}
|
||||
|
||||
// Tall angle brackets.
|
||||
&::before { content: "\276c "; }
|
||||
&::after { content: ' \276d'; }
|
||||
}
|
||||
|
||||
span.error {
|
||||
float: right;
|
||||
color: hsl(350, 50%, 60%);
|
||||
|
||||
&::before {
|
||||
color: hsl(350, 50%, 75%);
|
||||
content: "Error: ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Have a different primary color for the module docs.
|
||||
body.module {
|
||||
header {
|
||||
a {
|
||||
color: $gray-80;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $module-link-hover;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $module-link;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $module-link-dark;
|
||||
}
|
||||
|
||||
.header-anchor {
|
||||
color: $light;
|
||||
}
|
||||
|
||||
main {
|
||||
h1, h2, h3 {
|
||||
color: $module-link;
|
||||
}
|
||||
|
||||
h2:hover > .header-anchor:hover,
|
||||
h3:hover > .header-anchor:hover {
|
||||
color: $module-link-dark;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
a {
|
||||
color: $module-link-hover;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $module-link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
td, th {
|
||||
line-height: 14px;
|
||||
margin: 0;
|
||||
padding: 1px 0;
|
||||
}
|
||||
|
||||
th {
|
||||
font-size: 14px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.chart-bar {
|
||||
display: inline-block;
|
||||
font: 13px $body;
|
||||
color: $light;
|
||||
background: $link;
|
||||
border-bottom: solid 1px $link-dark;
|
||||
text-align: right;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.chart-bar.wren {
|
||||
background: mix($link, $link-dark, 30%);
|
||||
border-bottom: solid 1px $link-dark;
|
||||
}
|
||||
}
|
||||
|
||||
// Precedence table on expressions page.
|
||||
table.precedence {
|
||||
th {
|
||||
font: 500 11px $subheader;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: $gray-60;
|
||||
|
||||
padding: 6px 0;
|
||||
border-bottom: solid 1px $gray-10;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 3px 0;
|
||||
border-bottom: solid 1px $gray-10;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 839px) {
|
||||
// 36 pixel columns.
|
||||
.page { width: 720px; }
|
||||
nav { width: 144px; }
|
||||
.main-column { width: 504px; }
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 759px) {
|
||||
// 32 pixel columns.
|
||||
.page { width: 640px; }
|
||||
nav { width: 128px; }
|
||||
.main-column { width: 448px; }
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 679px) {
|
||||
// 28 pixel columns.
|
||||
.page { width: 560px; }
|
||||
nav { width: 112px; }
|
||||
.main-column { width: 392px; }
|
||||
|
||||
header h2 {
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 639px) {
|
||||
.page { width: 100%; }
|
||||
|
||||
// Switch to the mobile navigation.
|
||||
nav.big { display: none; }
|
||||
nav.small { display: block; }
|
||||
|
||||
.main-column {
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
h1 {
|
||||
position: relative;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,270 +0,0 @@
|
||||
^title Syntax
|
||||
|
||||
Wren's syntax is designed to be familiar to people coming from C-like languages
|
||||
while being a bit simpler and more streamlined.
|
||||
|
||||
Scripts are stored in plain text files with a `.wren` file extension. Wren does
|
||||
not compile ahead of time: programs are run directly from source, from top to
|
||||
bottom like a typical scripting language. (Internally, programs are compiled to
|
||||
bytecode for [efficiency][], but that's an implementation detail.)
|
||||
|
||||
[efficiency]: performance.html
|
||||
|
||||
## Comments
|
||||
|
||||
Line comments start with `//` and end at the end of the line:
|
||||
|
||||
:::wren
|
||||
// This is a comment.
|
||||
|
||||
Block comments start with `/*` and end with `*/`. They can span multiple lines:
|
||||
|
||||
:::wren
|
||||
/* This
|
||||
is
|
||||
a
|
||||
multi-line
|
||||
comment. */
|
||||
|
||||
Unlike C, block comments can nest in Wren:
|
||||
|
||||
:::wren
|
||||
/* This is /* a nested */ comment. */
|
||||
|
||||
This is handy because it lets you easily comment out an entire block of code,
|
||||
even if the code already contains block comments.
|
||||
|
||||
## Reserved words
|
||||
|
||||
One way to get a quick feel for a language's style is to see what words it
|
||||
reserves. Here's what Wren has:
|
||||
|
||||
:::wren
|
||||
break class construct else false for foreign if import
|
||||
in is null return static super this true var while
|
||||
|
||||
## Identifiers
|
||||
|
||||
Naming rules are similar to other programming languages. Identifiers start with
|
||||
a letter or underscore and may contain letters, digits, and underscores. Case
|
||||
is sensitive.
|
||||
|
||||
:::wren
|
||||
hi
|
||||
camelCase
|
||||
PascalCase
|
||||
_under_score
|
||||
abc123
|
||||
ALL_CAPS
|
||||
|
||||
Identifiers that start with underscore (`_`) are special in Wren. They are used
|
||||
to indicate [fields](classes.html#fields) in classes.
|
||||
|
||||
## Newlines
|
||||
|
||||
Newlines (`\n`) are meaningful in Wren. They are used to separate statements:
|
||||
|
||||
:::wren
|
||||
// Two statements:
|
||||
System.print("hi") // Newline.
|
||||
System.print("bye")
|
||||
|
||||
Sometimes, though, a statement doesn't fit on a single line and jamming a
|
||||
newline in the middle would trip it up. To handle that, Wren has a very simple
|
||||
rule: It ignores a newline following any token that can't end a statement.
|
||||
|
||||
:::wren
|
||||
System.print( // Newline here is ignored.
|
||||
"hi")
|
||||
|
||||
In practice, this means you can put each statement on its own line and wrap
|
||||
them across lines as needed without too much trouble.
|
||||
|
||||
## Blocks
|
||||
|
||||
Wren uses curly braces to define *blocks*. You can use a block anywhere a
|
||||
statement is allowed, like in [control flow](control-flow.html) statements.
|
||||
[Method](classes.html#methods) and [function](functions.html) bodies are also
|
||||
blocks. For example, here we have a block for the then case, and a single
|
||||
statement for the else:
|
||||
|
||||
:::wren
|
||||
if (happy && knowIt) {
|
||||
hands.clap()
|
||||
} else System.print("sad")
|
||||
|
||||
Blocks have two similar but not identical forms. Typically, blocks contain a
|
||||
series of statements like:
|
||||
|
||||
:::wren
|
||||
{
|
||||
System.print("one")
|
||||
System.print("two")
|
||||
System.print("three")
|
||||
}
|
||||
|
||||
Blocks of this form when used for method and function bodies automatically
|
||||
return `null` after the block has completed. If you want to return a different
|
||||
value, you need an explicit `return` statement.
|
||||
|
||||
However, it's pretty common to have a method or function that just evaluates and
|
||||
returns the result of a single expression. Some other languages use `=>` to
|
||||
define these. Wren uses:
|
||||
|
||||
:::wren
|
||||
{ "single expression" }
|
||||
|
||||
If there is no newline after the `{` (or after the parameter list in a
|
||||
[function](functions.html)), then the block may only contain a single
|
||||
expression, and it automatically returns the result of it. It's exactly the same
|
||||
as doing:
|
||||
|
||||
:::wren
|
||||
{
|
||||
return "single expression"
|
||||
}
|
||||
|
||||
Statements are not allowed in this form (since they don't produce values), which
|
||||
means nothing starting with `class`, `for`, `if`, `import`, `return`,
|
||||
`var`, or `while`. If you want a block that contains a single statement,
|
||||
put a newline in there:
|
||||
|
||||
:::wren
|
||||
{
|
||||
if (happy) {
|
||||
System.print("I'm feelin' it!")
|
||||
}
|
||||
}
|
||||
|
||||
Using an initial newline after the `{` does feel a little weird or magical, but
|
||||
newlines are already significant in Wren, so it's not totally crazy. The nice
|
||||
thing about this syntax as opposed to something like `=>` is that the *end* of
|
||||
the block has an explicit delimiter. That helps when chaining:
|
||||
|
||||
:::wren
|
||||
numbers.map {|n| n * 2 }.where {|n| n < 100 }
|
||||
|
||||
## Precedence and Associativity
|
||||
|
||||
We'll talk about Wren's different expression forms and what they mean in the
|
||||
next few pages. But if you want to see how they interact with each other
|
||||
grammatically, here's the whole table.
|
||||
|
||||
It shows which expressions have higher *precedence*—which ones bind more
|
||||
tightly than others—and their *associativity*—how a series of the
|
||||
same kind of expression is ordered. Wren mostly follows C, except that it fixes
|
||||
[the bitwise operator mistake][mistake]. The full precedence table, from
|
||||
tightest to loosest, is:
|
||||
|
||||
[mistake]: http://www.lysator.liu.se/c/dmr-on-or.html
|
||||
|
||||
<table class="precedence">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Prec</th>
|
||||
<th>Operator</th>
|
||||
<th>Description</th>
|
||||
<th>Associates</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td><code>()</code> <code>[]</code> <code>.</code></td>
|
||||
<td>Grouping, <a href="method-calls.html">Subscript, Method call</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td><code>-</code> <code>!</code> <code>~</code></td>
|
||||
<td><a href="method-calls.html#operators">Negate, Not, Complement</a></td>
|
||||
<td>Right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td><code>*</code> <code>/</code> <code>%</code></td>
|
||||
<td><a href="method-calls.html#operators">Multiply, Divide, Modulo</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td><code>+</code> <code>-</code></td>
|
||||
<td><a href="method-calls.html#operators">Add, Subtract</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td><code>..</code> <code>...</code></td>
|
||||
<td><a href="method-calls.html#operators">Inclusive range, Exclusive range</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td><code><<</code> <code>>></code></td>
|
||||
<td><a href="method-calls.html#operators">Left shift, Right shift</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td><code>&</code></td>
|
||||
<td><a href="method-calls.html#operators">Bitwise and</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td><code>^</code></td>
|
||||
<td><a href="method-calls.html#operators">Bitwise xor</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>9</td>
|
||||
<td><code>|</code></td>
|
||||
<td><a href="method-calls.html#operators">Bitwise or</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>10</td>
|
||||
<td><code><</code> <code><=</code> <code>></code> <code>>=</code></td>
|
||||
<td><a href="method-calls.html#operators">Comparison</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11</td>
|
||||
<td><code>is</code></td>
|
||||
<td><a href="method-calls.html#operators">Type test</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>12</td>
|
||||
<td><code>==</code> <code>!=</code></td>
|
||||
<td><a href="method-calls.html#operators">Equals, Not equal</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>13</td>
|
||||
<td><code>&&</code></td>
|
||||
<td><a href="control-flow.html#logical-operators">Logical and</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>14</td>
|
||||
<td><code>||</code></td>
|
||||
<td><a href="control-flow.html#logical-operators">Logical or</a></td>
|
||||
<td>Left</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>15</td>
|
||||
<td><code>?:</code></td>
|
||||
<td><a href="control-flow.html#the-conditional-operator-">Conditional</a></td>
|
||||
<td>Right</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>16</td>
|
||||
<td><code>=</code></td>
|
||||
<td><a href="variables.html#assignment">Assignment</a>, <a href="method-calls.html#setters">Setter</a></td>
|
||||
<td>Right</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<br><hr>
|
||||
<a class="right" href="values.html">Values →</a>
|
||||
<a href="getting-started.html">← Getting Started</a>
|
||||
@ -1,117 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<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>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>guides</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="getting-started.html">Getting Started</a></div>
|
||||
<div><a href="contributing.html">Contributing</a></div>
|
||||
<div><a href="blog">Blog</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>guides</h2></td>
|
||||
<td><h2>reference</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</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>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,142 +0,0 @@
|
||||
^title Values
|
||||
|
||||
Values are the built-in atomic object types that all other objects are composed
|
||||
of. They can be created through *literals*, expressions that evaluate to a
|
||||
value. All values are *immutable*—once created, they do not change. The
|
||||
number `3` is always the number `3`. The string `"frozen"` can never have its
|
||||
character array modified in place.
|
||||
|
||||
## Booleans
|
||||
|
||||
A boolean value represents truth or falsehood. There are two boolean literals,
|
||||
`true` and `false`. Their class is [Bool][].
|
||||
|
||||
[bool]: modules/core/bool.html
|
||||
|
||||
## Numbers
|
||||
|
||||
Like other scripting languages, Wren has a single numeric type:
|
||||
double-precision floating point. Number literals look like you expect coming
|
||||
from other languages:
|
||||
|
||||
:::wren
|
||||
0
|
||||
1234
|
||||
-5678
|
||||
3.14159
|
||||
1.0
|
||||
-12.34
|
||||
|
||||
Numbers are instances of the [Num][] class.
|
||||
|
||||
[num]: modules/core/num.html
|
||||
|
||||
## Strings
|
||||
|
||||
A string is an array of bytes. Typically, they store characters encoded in
|
||||
UTF-8, but you can put any byte values in there, even zero or invalid UTF-8
|
||||
sequences. (You might have some trouble *printing* the latter to your terminal,
|
||||
though.)
|
||||
|
||||
String literals are surrounded in double quotes:
|
||||
|
||||
:::wren
|
||||
"hi there"
|
||||
|
||||
A handful of escape characters are supported:
|
||||
|
||||
:::wren
|
||||
"\0" // The NUL byte: 0.
|
||||
"\"" // A double quote character.
|
||||
"\\" // A backslash.
|
||||
"\%" // A percent sign.
|
||||
"\a" // Alarm beep. (Who uses this?)
|
||||
"\b" // Backspace.
|
||||
"\f" // Formfeed.
|
||||
"\n" // Newline.
|
||||
"\r" // Carriage return.
|
||||
"\t" // Tab.
|
||||
"\v" // Vertical tab.
|
||||
|
||||
A `\u` followed by four hex digits can be used to specify a Unicode code point:
|
||||
|
||||
:::wren
|
||||
System.print("\u0041\u0b83\u00DE") //> AஃÞ
|
||||
|
||||
A capital `\U` followed by *eight* hex digits allows Unicode code points outside
|
||||
of the basic multilingual plane, like all-important emoji:
|
||||
|
||||
:::wren
|
||||
System.print("\U0001F64A\U0001F680") //> 🙊🚀
|
||||
|
||||
A `\x` followed by two hex digits specifies a single unencoded byte:
|
||||
|
||||
:::wren
|
||||
System.print("\x48\x69\x2e") //> Hi.
|
||||
|
||||
Strings are instances of class [String][].
|
||||
|
||||
[string]: modules/core/string.html
|
||||
|
||||
### Interpolation
|
||||
|
||||
String literals also allow *interpolation*. If you have a percent sign (`%`)
|
||||
followed by a parenthesized expression, the expression is evaluated. The
|
||||
resulting object's `toString` method is called and the result is inserted in the
|
||||
string:
|
||||
|
||||
:::wren
|
||||
System.print("Math %(3 + 4 * 5) is fun!") //> Math 23 is fun!
|
||||
|
||||
Arbitrarily complex expressions are allowed inside the parentheses:
|
||||
|
||||
:::wren
|
||||
System.print("wow %((1..3).map {|n| n * n}.join())") //> wow 149
|
||||
|
||||
An interpolated expression can even contain a string literal which in turn has
|
||||
its own nested interpolations, but doing that gets unreadable pretty quickly.
|
||||
|
||||
## Ranges
|
||||
|
||||
A range is a little object that represents a consecutive range of numbers. They
|
||||
don't have their own dedicated literal syntax. Instead, the number class
|
||||
implements the `..` and `...` [operators][] to create them:
|
||||
|
||||
[operators]: method-calls.html#operators
|
||||
|
||||
:::wren
|
||||
3..8
|
||||
|
||||
This creates a range from three to eight, including eight itself. If you want a
|
||||
half-inclusive range, use `...`:
|
||||
|
||||
:::wren
|
||||
4...6
|
||||
|
||||
This creates a range from four to six *not* including six itself. Ranges are
|
||||
commonly used for [iterating](control-flow.html#for-statements) over a
|
||||
sequences of numbers, but are useful in other places too. You can pass them to
|
||||
a [list](lists.html)'s subscript operator to return a subset of the list, for
|
||||
example:
|
||||
|
||||
:::wren
|
||||
var list = ["a", "b", "c", "d", "e"]
|
||||
var slice = list[1..3]
|
||||
System.print(slice) //> [b, c, d]
|
||||
|
||||
Their class is [Range][].
|
||||
|
||||
[range]: modules/core/range.html
|
||||
|
||||
## Null
|
||||
|
||||
Wren has a special value `null`, which is the only instance of the class
|
||||
[Null][]. (Note the difference in case.) It functions a bit like `void` in some
|
||||
languages: it indicates the absence of a value. If you call a method that
|
||||
doesn't return anything and get its returned value, you get `null` back.
|
||||
|
||||
[null]: modules/core/null.html
|
||||
|
||||
<br><hr>
|
||||
<a class="right" href="lists.html">Lists →</a>
|
||||
<a href="syntax.html">← Syntax</a>
|
||||
@ -1,76 +0,0 @@
|
||||
^title Variables
|
||||
|
||||
Variables are named slots for storing values. You define a new variable in Wren
|
||||
using a `var` statement, like so:
|
||||
|
||||
:::wren
|
||||
var a = 1 + 2
|
||||
|
||||
This creates a new variable `a` in the current scope and initializes it with
|
||||
the result of the expression following the `=`. Once a variable has been
|
||||
defined, it can be accessed by name as you would expect.
|
||||
|
||||
:::wren
|
||||
var animal = "Slow Loris"
|
||||
System.print(animal) //> Slow Loris
|
||||
|
||||
## Scope
|
||||
|
||||
Wren has true block scope: a variable exists from the point where it is defined
|
||||
until the end of the [block](syntax.html#blocks) where that definition appears.
|
||||
|
||||
:::wren
|
||||
{
|
||||
System.print(a) //! "a" doesn't exist yet.
|
||||
var a = 123
|
||||
System.print(a) //> 123
|
||||
}
|
||||
System.print(a) //! "a" doesn't exist anymore.
|
||||
|
||||
Variables defined at the top level of a script are *top-level* and are visible
|
||||
to the [module](modules.html) system. All other variables are *local*.
|
||||
Declaring a variable in an inner scope with the same name as an outer one is
|
||||
called *shadowing* and is not an error (although it's not something you likely
|
||||
intend to do much).
|
||||
|
||||
:::wren
|
||||
var a = "outer"
|
||||
{
|
||||
var a = "inner"
|
||||
System.print(a) //> inner
|
||||
}
|
||||
System.print(a) //> outer
|
||||
|
||||
Declaring a variable with the same name in the *same* scope *is* an error.
|
||||
|
||||
:::wren
|
||||
var a = "hi"
|
||||
var a = "again" //! "a" is already declared.
|
||||
|
||||
## Assignment
|
||||
|
||||
After a variable has been declared, you can assign to it using `=`:
|
||||
|
||||
:::wren
|
||||
var a = 123
|
||||
a = 234
|
||||
|
||||
An assignment walks up the scope stack to find where the named variable is
|
||||
declared. It's an error to assign to a variable that isn't defined. Wren
|
||||
doesn't roll with implicit variable definition.
|
||||
|
||||
When used in a larger expression, an assignment expression evaluates to the
|
||||
assigned value.
|
||||
|
||||
:::wren
|
||||
var a = "before"
|
||||
System.print(a = "after") //> after
|
||||
|
||||
If the left-hand side is some more complex expression than a bare variable name,
|
||||
then it isn't an assignment. Instead, it's calling a [setter method][].
|
||||
|
||||
[setter method]: method-calls.html#setters
|
||||
|
||||
<br><hr>
|
||||
<a class="right" href="functions.html">Functions →</a>
|
||||
<a href="control-flow.html">← Control Flow</a>
|
||||
@ -1,93 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Runs GYP to generate the right project then uses that to build libuv.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from util import ensure_dir, python2_binary, run
|
||||
|
||||
LIB_UV_VERSION = "v1.10.0"
|
||||
LIB_UV_DIR = "deps/libuv"
|
||||
|
||||
|
||||
def build_libuv_mac():
|
||||
# Create the XCode project.
|
||||
run([
|
||||
python2_binary(), LIB_UV_DIR + "/gyp_uv.py", "-f", "xcode"
|
||||
])
|
||||
|
||||
# Compile it.
|
||||
# TODO: Support debug builds too.
|
||||
run([
|
||||
"xcodebuild",
|
||||
# Build a 32-bit + 64-bit universal binary:
|
||||
"ARCHS=x86_64", "ONLY_ACTIVE_ARCH=NO",
|
||||
"BUILD_DIR=out",
|
||||
"-project", LIB_UV_DIR + "/uv.xcodeproj",
|
||||
"-configuration", "Release",
|
||||
"-target", "libuv"
|
||||
])
|
||||
|
||||
|
||||
def build_libuv_linux(arch):
|
||||
# Set up the Makefile to build for the right architecture.
|
||||
args = [python2_binary(), "gyp_uv.py", "-f", "make"]
|
||||
if arch == "-32":
|
||||
args.append("-Dtarget_arch=ia32")
|
||||
elif arch == "-64":
|
||||
args.append("-Dtarget_arch=x64")
|
||||
|
||||
run(args, cwd=LIB_UV_DIR)
|
||||
run(["make", "-C", "out", "BUILDTYPE=Release", "libuv"], cwd=LIB_UV_DIR)
|
||||
|
||||
|
||||
def build_libuv_windows(arch):
|
||||
args = ["cmd", "/c", "vcbuild.bat", "release", "vs2017"]
|
||||
if arch == "-32":
|
||||
args.append("x86")
|
||||
elif arch == "-64":
|
||||
args.append("x64")
|
||||
run(args, cwd=LIB_UV_DIR)
|
||||
|
||||
|
||||
def build_libuv(arch, out):
|
||||
if platform.system() == "Darwin":
|
||||
build_libuv_mac()
|
||||
elif platform.system() == "Linux":
|
||||
build_libuv_linux(arch)
|
||||
elif platform.system() == "Windows":
|
||||
build_libuv_windows(arch)
|
||||
else:
|
||||
print("Unsupported platform: " + platform.system())
|
||||
sys.exit(1)
|
||||
|
||||
# Copy the build library to the build directory for Mac and Linux where we
|
||||
# support building for multiple architectures.
|
||||
if platform.system() != "Windows":
|
||||
ensure_dir(os.path.dirname(out))
|
||||
shutil.copyfile(
|
||||
os.path.join(LIB_UV_DIR, "out", "Release", "libuv.a"), out)
|
||||
|
||||
|
||||
def main(args):
|
||||
expect_usage(len(args) >= 1 and len(args) <= 2)
|
||||
|
||||
arch = "" if len(args) < 2 else args[1]
|
||||
out = os.path.join("build", "libuv" + arch + ".a")
|
||||
|
||||
build_libuv(arch, out)
|
||||
|
||||
|
||||
def expect_usage(condition):
|
||||
if (condition): return
|
||||
|
||||
print("Usage: build_libuv.py [-32|-64]")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
main(sys.argv)
|
||||
@ -1,33 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Install the Wren Pygments lexer.
|
||||
cd util/pygments-lexer
|
||||
sudo python3 setup.py develop
|
||||
cd ../..
|
||||
|
||||
# Build the docs.
|
||||
make gh-pages
|
||||
|
||||
# Clone the repo at the gh-pages branch.
|
||||
git clone https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG} gh-pages-repo \
|
||||
--branch gh-pages --depth 1
|
||||
cd gh-pages-repo
|
||||
|
||||
# Copy them into the gh-pages branch.
|
||||
rm -rf *
|
||||
cp -r ../build/gh-pages/* .
|
||||
|
||||
# Restore CNAME file that gets deleted by `rm -rf *`.
|
||||
echo "wren.io" > "CNAME"
|
||||
|
||||
git status
|
||||
ls
|
||||
|
||||
if ! $( git diff-index --quiet HEAD ) ; then
|
||||
git config user.name "Travis CI"
|
||||
git config user.email "$COMMIT_AUTHOR_EMAIL"
|
||||
git add --all .
|
||||
git commit -m "Deploy to GitHub Pages: ${SHA}"
|
||||
git push
|
||||
fi
|
||||
@ -1,73 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
from os.path import basename, dirname, join, realpath, isfile
|
||||
from glob import iglob
|
||||
import re
|
||||
|
||||
INCLUDE_PATTERN = re.compile(r'^\s*#include "([\w.]+)"')
|
||||
GUARD_PATTERN = re.compile(r'^#ifndef wren(_\w+)?_h$')
|
||||
WREN_DIR = dirname(dirname(realpath(__file__)))
|
||||
|
||||
seen_files = set()
|
||||
out = sys.stdout
|
||||
|
||||
# Find a file in the different folders of the src dir.
|
||||
def find_file(filename):
|
||||
names = [
|
||||
join(WREN_DIR, 'src', 'include', filename),
|
||||
join(WREN_DIR, 'src', 'vm', filename),
|
||||
join(WREN_DIR, 'src', 'optional', filename),
|
||||
]
|
||||
for f in names:
|
||||
if isfile(f):
|
||||
return f
|
||||
raise Exception('File "{0}" not found!'.format(filename))
|
||||
|
||||
# Prints a plain text file, adding comment markers.
|
||||
def add_comment_file(filename):
|
||||
with open(filename, 'r') as f:
|
||||
for line in f:
|
||||
out.write('// ')
|
||||
out.write(line)
|
||||
|
||||
# Prints the given C source file, recursively resolving local #includes.
|
||||
def add_file(filename):
|
||||
bname = basename(filename)
|
||||
# Only include each file at most once.
|
||||
if bname in seen_files:
|
||||
return
|
||||
once = False
|
||||
|
||||
out.write('// Begin file "{0}"\n'.format(bname))
|
||||
with open(filename, 'r') as f:
|
||||
for line in f:
|
||||
m = INCLUDE_PATTERN.match(line)
|
||||
if m:
|
||||
add_file(find_file(m.group(1)))
|
||||
else:
|
||||
out.write(line)
|
||||
if GUARD_PATTERN.match(line):
|
||||
once = True
|
||||
out.write('// End file "{0}"\n'.format(bname))
|
||||
|
||||
# Only skip header files which use #ifndef guards.
|
||||
# This is necessary because of the X Macro technique.
|
||||
if once:
|
||||
seen_files.add(bname)
|
||||
|
||||
# Print license on top.
|
||||
add_comment_file(join(WREN_DIR, 'LICENSE'))
|
||||
out.write('\n')
|
||||
|
||||
# Source files.
|
||||
add_file(join(WREN_DIR, 'src', 'include', 'wren.h'))
|
||||
|
||||
# Must be included here because of conditional compilation.
|
||||
add_file(join(WREN_DIR, 'src', 'vm', 'wren_debug.h'))
|
||||
|
||||
for f in iglob(join(WREN_DIR, 'src', 'vm', '*.c')):
|
||||
add_file(f)
|
||||
|
||||
for f in iglob(join(WREN_DIR, 'src', 'optional', '*.c')):
|
||||
add_file(f)
|
||||
@ -1,224 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import codecs
|
||||
import glob
|
||||
import fnmatch
|
||||
import os
|
||||
import posixpath
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import re
|
||||
import urllib
|
||||
from datetime import datetime
|
||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||
|
||||
import markdown
|
||||
|
||||
|
||||
# Match a "## " style header. We require a space after "#" to avoid
|
||||
# accidentally matching "#include" in code samples.
|
||||
MARKDOWN_HEADER = re.compile(r'#+ ')
|
||||
|
||||
# Clean up a header to be a valid URL.
|
||||
FORMAT_ANCHOR = re.compile(r'\?|!|:|/|\*|`')
|
||||
|
||||
|
||||
class RootedHTTPServer(HTTPServer):
|
||||
"""Simple server that resolves paths relative to a given directory.
|
||||
|
||||
From: http://louistiao.me/posts/python-simplehttpserver-recipe-serve-specific-directory/
|
||||
"""
|
||||
def __init__(self, base_path, *args, **kwargs):
|
||||
HTTPServer.__init__(self, *args, **kwargs)
|
||||
self.RequestHandlerClass.base_path = base_path
|
||||
|
||||
|
||||
class RootedHTTPRequestHandler(SimpleHTTPRequestHandler):
|
||||
"""Simple handler that resolves paths relative to a given directory.
|
||||
|
||||
From: http://louistiao.me/posts/python-simplehttpserver-recipe-serve-specific-directory/
|
||||
"""
|
||||
def translate_path(self, path):
|
||||
# Refresh files that are being requested.
|
||||
format_files(True)
|
||||
|
||||
path = posixpath.normpath(urllib.parse.unquote(path))
|
||||
words = path.split('/')
|
||||
words = filter(None, words)
|
||||
path = self.base_path
|
||||
for word in words:
|
||||
drive, word = os.path.splitdrive(word)
|
||||
head, word = os.path.split(word)
|
||||
if word in (os.curdir, os.pardir):
|
||||
continue
|
||||
path = os.path.join(path, word)
|
||||
return path
|
||||
|
||||
|
||||
def ensure_dir(path):
|
||||
if not os.path.exists(path):
|
||||
os.mkdir(path)
|
||||
|
||||
|
||||
def is_up_to_date(path, out_path):
|
||||
dest_mod = 0
|
||||
if os.path.exists(out_path):
|
||||
dest_mod = os.path.getmtime(out_path)
|
||||
|
||||
# See if it's up to date.
|
||||
source_mod = os.path.getmtime(path)
|
||||
return source_mod < dest_mod
|
||||
|
||||
|
||||
def format_file(path, skip_up_to_date):
|
||||
in_path = os.path.join('doc/site', path)
|
||||
out_path = "build/docs/" + os.path.splitext(path)[0] + ".html"
|
||||
template_path = os.path.join("doc/site", os.path.dirname(path),
|
||||
"template.html")
|
||||
|
||||
if (skip_up_to_date and
|
||||
is_up_to_date(in_path, out_path) and
|
||||
is_up_to_date(template_path, out_path)):
|
||||
# It's up to date.
|
||||
return
|
||||
|
||||
title = ""
|
||||
|
||||
# Read the markdown file and preprocess it.
|
||||
contents = ""
|
||||
with codecs.open(in_path, "r", encoding="utf-8") as input:
|
||||
# Read each line, preprocessing the special codes.
|
||||
for line in input:
|
||||
stripped = line.lstrip()
|
||||
indentation = line[:len(line) - len(stripped)]
|
||||
|
||||
if stripped.startswith("^"):
|
||||
command,_,args = stripped.rstrip("\n").lstrip("^").partition(" ")
|
||||
args = args.strip()
|
||||
|
||||
if command == "title":
|
||||
title = args
|
||||
else:
|
||||
print(' '.join(["UNKNOWN COMMAND:", command, args]))
|
||||
|
||||
elif MARKDOWN_HEADER.match(stripped):
|
||||
# Add anchors to the headers.
|
||||
index = stripped.find(" ")
|
||||
headertype = stripped[:index]
|
||||
header = stripped[index:].strip()
|
||||
anchor = header.lower().replace(' ', '-')
|
||||
anchor = FORMAT_ANCHOR.sub('', anchor)
|
||||
|
||||
contents += indentation + headertype
|
||||
contents += '{1} <a href="#{0}" name="{0}" class="header-anchor">#</a>\n'.format(anchor, header)
|
||||
|
||||
else:
|
||||
# Forcibly add a space to the end of each line. Works around a bug in
|
||||
# the smartypants extension that removes some newlines that are needed.
|
||||
# https://github.com/waylan/Python-Markdown/issues/439
|
||||
if "//" not in line:
|
||||
contents = contents + line.rstrip() + ' \n'
|
||||
else:
|
||||
# Don't add a trailing space on comment lines since they may be
|
||||
# output lines which have a trailing ">" which makes the extra space
|
||||
# visible.
|
||||
contents += line
|
||||
|
||||
html = markdown.markdown(contents, extensions=['def_list', 'codehilite', 'smarty'])
|
||||
|
||||
# Use special formatting for example output and errors.
|
||||
html = html.replace('<span class="c1">//> ', '<span class="output">')
|
||||
html = html.replace('<span class="c1">//&gt; ', '<span class="output">')
|
||||
html = html.replace('<span class="c1">//! ', '<span class="error">')
|
||||
|
||||
modified = datetime.fromtimestamp(os.path.getmtime(in_path))
|
||||
mod_str = modified.strftime('%B %d, %Y')
|
||||
|
||||
with codecs.open(template_path, encoding="utf-8") as f:
|
||||
page_template = f.read()
|
||||
|
||||
fields = {
|
||||
'title': title,
|
||||
'html': html,
|
||||
'mod': mod_str
|
||||
}
|
||||
|
||||
# Write the html output.
|
||||
ensure_dir(os.path.dirname(out_path))
|
||||
|
||||
with codecs.open(out_path, "w", encoding="utf-8") as out:
|
||||
out.write(page_template.format(**fields))
|
||||
|
||||
print("Built " + path)
|
||||
|
||||
|
||||
def check_sass():
|
||||
source_mod = os.path.getmtime('doc/site/style.scss')
|
||||
|
||||
dest_mod = 0
|
||||
if os.path.exists('build/docs/style.css'):
|
||||
dest_mod = os.path.getmtime('build/docs/style.css')
|
||||
|
||||
if source_mod < dest_mod:
|
||||
return
|
||||
|
||||
subprocess.call(['sass', 'doc/site/style.scss', 'build/docs/style.css'])
|
||||
print("Built build/docs/style.css")
|
||||
|
||||
|
||||
def copy_static():
|
||||
shutil.copy2("doc/site/blog/rss.xml", "build/docs/blog/rss.xml")
|
||||
|
||||
for root, dirnames, filenames in os.walk('doc/site/static'):
|
||||
for filename in filenames:
|
||||
source = os.path.join(root, filename)
|
||||
source_mod = os.path.getmtime(source)
|
||||
dest = os.path.join("build/docs", filename)
|
||||
dest_mod = 0
|
||||
if os.path.exists(dest):
|
||||
dest_mod = os.path.getmtime('build/docs/style.css')
|
||||
|
||||
if source_mod < dest_mod:
|
||||
return
|
||||
|
||||
shutil.copy2(source, dest)
|
||||
print('Copied ' + filename)
|
||||
|
||||
def format_files(skip_up_to_date):
|
||||
check_sass()
|
||||
|
||||
for root, dirnames, filenames in os.walk('doc/site'):
|
||||
for filename in fnmatch.filter(filenames, '*.markdown'):
|
||||
f = os.path.relpath(os.path.join(root, filename), 'doc/site')
|
||||
format_file(f, skip_up_to_date)
|
||||
|
||||
copy_static()
|
||||
|
||||
def run_server():
|
||||
port = 8000
|
||||
handler = RootedHTTPRequestHandler
|
||||
server = RootedHTTPServer("build/docs", ('localhost', port), handler)
|
||||
|
||||
print('Serving at port', port)
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
# Clean the output directory.
|
||||
if os.path.exists("build/docs"):
|
||||
shutil.rmtree("build/docs")
|
||||
ensure_dir("build/docs")
|
||||
|
||||
# Process each markdown file.
|
||||
format_files(False)
|
||||
|
||||
# Watch and serve files.
|
||||
if len(sys.argv) == 2 and sys.argv[1] == '--serve':
|
||||
run_server()
|
||||
|
||||
# Watch files.
|
||||
if len(sys.argv) == 2 and sys.argv[1] == '--watch':
|
||||
while True:
|
||||
format_files(True)
|
||||
time.sleep(0.3)
|
||||
@ -45,7 +45,7 @@ def c_metrics(label, directories):
|
||||
for source_path in files:
|
||||
num_files += 1
|
||||
|
||||
with open(source_path, "r") as input:
|
||||
with open(source_path, "r", encoding="utf-8") as input:
|
||||
for line in input:
|
||||
num_semicolons += line.count(';')
|
||||
match = TODO_PATTERN.match(line)
|
||||
@ -84,10 +84,17 @@ def wren_metrics(label, directories):
|
||||
for directory in directories:
|
||||
for dir_path, dir_names, file_names in os.walk(directory):
|
||||
for file_name in fnmatch.filter(file_names, "*.wren"):
|
||||
file_path = os.path.join(dir_path, file_name)
|
||||
file_path = file_path.replace('\\', '/')
|
||||
|
||||
# print(file_path)
|
||||
|
||||
num_files += 1
|
||||
|
||||
with open(os.path.join(dir_path, file_name), "r") as input:
|
||||
for line in input:
|
||||
with open(file_path, "r", encoding="utf-8", newline='', errors='replace') as input:
|
||||
data = input.read()
|
||||
lines = re.split('\n|\r\n', data)
|
||||
for line in lines:
|
||||
if line.strip() == "":
|
||||
num_empty += 1
|
||||
continue
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: Wren
|
||||
Version: 1.0
|
||||
Summary:
|
||||
A Pygments lexer for Wren.
|
||||
|
||||
Home-page: UNKNOWN
|
||||
Author: Robert Nystrom
|
||||
Author-email: UNKNOWN
|
||||
License: UNKNOWN
|
||||
Description-Content-Type: UNKNOWN
|
||||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
@ -1,7 +0,0 @@
|
||||
setup.py
|
||||
Wren.egg-info/PKG-INFO
|
||||
Wren.egg-info/SOURCES.txt
|
||||
Wren.egg-info/dependency_links.txt
|
||||
Wren.egg-info/entry_points.txt
|
||||
Wren.egg-info/top_level.txt
|
||||
wren/__init__.py
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
|
||||
[pygments.lexers]
|
||||
wrenlexer = wren:WrenLexer
|
||||
|
||||
@ -1 +0,0 @@
|
||||
wren
|
||||
@ -1,18 +0,0 @@
|
||||
"""
|
||||
A Pygments lexer for Wren.
|
||||
"""
|
||||
from setuptools import setup
|
||||
|
||||
__author__ = 'Robert Nystrom'
|
||||
|
||||
setup(
|
||||
name='Wren',
|
||||
version='1.0',
|
||||
description=__doc__,
|
||||
author=__author__,
|
||||
packages=['wren'],
|
||||
entry_points='''
|
||||
[pygments.lexers]
|
||||
wrenlexer = wren:WrenLexer
|
||||
'''
|
||||
)
|
||||
@ -1,82 +0,0 @@
|
||||
import re
|
||||
from pygments import highlight
|
||||
from pygments.lexers import PythonLexer
|
||||
from pygments.formatters import HtmlFormatter
|
||||
|
||||
from pygments.lexer import RegexLexer
|
||||
from pygments.token import *
|
||||
|
||||
class WrenLexer(RegexLexer):
|
||||
name = 'Wren'
|
||||
aliases = ['wren']
|
||||
filenames = ['*.wren']
|
||||
|
||||
flags = re.MULTILINE | re.DOTALL
|
||||
|
||||
tokens = {
|
||||
'root': [
|
||||
# Whitespace.
|
||||
(r'\s+', Text),
|
||||
(r'[,\\\[\]{}]', Punctuation),
|
||||
|
||||
# Push a parenthesized state so that we know the corresponding ')'
|
||||
# is for a parenthesized expression and not interpolation.
|
||||
(r'\(', Punctuation, ('parenthesized', 'root')),
|
||||
|
||||
# In this state, we don't know whether a closing ')' is for a
|
||||
# parenthesized expression or the end of an interpolation. So, do
|
||||
# a non-consuming match and let the parent state (either
|
||||
# 'parenthesized' or 'interpolation' decide.
|
||||
(r'(?=\))', Text, '#pop'),
|
||||
|
||||
# Keywords.
|
||||
(r'(break|class|construct|else|for|foreign|if|import|in|is|'
|
||||
r'return|static|super|var|while)\b', Keyword),
|
||||
|
||||
(r'(true|false|null)\b', Keyword.Constant),
|
||||
|
||||
(r'this\b', Name.Builtin),
|
||||
|
||||
# Comments.
|
||||
(r'/\*', Comment.Multiline, 'comment'),
|
||||
(r'//.*?$', Comment.Single),
|
||||
|
||||
# Names and operators.
|
||||
(r'[~!$%^&*\-=+\\|/?<>\.:]+', Operator),
|
||||
(r'[A-Z][a-zA-Z_0-9]+', Name.Variable.Global),
|
||||
(r'__[a-zA-Z_0-9]+', Name.Variable.Class),
|
||||
(r'_[a-zA-Z_0-9]+', Name.Variable.Instance),
|
||||
(r'[a-z][a-zA-Z_0-9]+', Name),
|
||||
|
||||
# Numbers.
|
||||
(r'\d+\.\d+([eE]-?\d+)?', Number.Float),
|
||||
(r'0x[0-9a-fA-F]+', Number.Hex),
|
||||
(r'\d+', Number.Integer),
|
||||
|
||||
# Strings.
|
||||
(r'L?"', String, 'string'),
|
||||
],
|
||||
'comment': [
|
||||
(r'/\*', Comment.Multiline, '#push'),
|
||||
(r'\*/', Comment.Multiline, '#pop'),
|
||||
(r'.', Comment.Multiline), # All other characters.
|
||||
],
|
||||
'string': [
|
||||
(r'"', String, '#pop'),
|
||||
(r'\\[\\%0abfnrtv"\']', String.Escape), # Escape.
|
||||
(r'\\x[a-fA-F0-9]{2}', String.Escape), # Byte escape.
|
||||
(r'\\u[a-fA-F0-9]{4}', String.Escape), # Unicode escape.
|
||||
(r'\\U[a-fA-F0-9]{8}', String.Escape), # Long Unicode escape.
|
||||
|
||||
(r'%\(', String.Interpol, ('interpolation', 'root')),
|
||||
(r'.', String), # All other characters.
|
||||
],
|
||||
'parenthesized': [
|
||||
# We only get to this state when we're at a ')'.
|
||||
(r'\)', Punctuation, '#pop'),
|
||||
],
|
||||
'interpolation': [
|
||||
# We only get to this state when we're at a ')'.
|
||||
(r'\)', String.Interpol, '#pop'),
|
||||
],
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Downloads GYP and libuv into deps/.
|
||||
#
|
||||
# Run this manually to update the vendored copies of GYP and libuv that are
|
||||
# committed in the Wren repo.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
|
||||
from util import clean_dir, remove_dir, replace_in_file, run
|
||||
|
||||
LIB_UV_VERSION = "v1.10.0"
|
||||
LIB_UV_DIR = "deps/libuv"
|
||||
|
||||
|
||||
def main(args):
|
||||
# Delete it if already there so we ensure we get the correct version if the
|
||||
# version number in this script changes.
|
||||
clean_dir("deps")
|
||||
|
||||
print("Cloning libuv...")
|
||||
run([
|
||||
"git", "clone", "--quiet", "--depth=1",
|
||||
"https://github.com/libuv/libuv.git",
|
||||
LIB_UV_DIR
|
||||
])
|
||||
|
||||
print("Getting tags...")
|
||||
run([
|
||||
"git", "fetch", "--quiet", "--depth=1", "--tags"
|
||||
], cwd=LIB_UV_DIR)
|
||||
|
||||
print("Checking out libuv " + LIB_UV_VERSION + "...")
|
||||
run([
|
||||
"git", "checkout", "--quiet", LIB_UV_VERSION
|
||||
], cwd=LIB_UV_DIR)
|
||||
|
||||
|
||||
# TODO: Pin gyp to a known-good commit. Update a previously downloaded gyp
|
||||
# if it doesn't match that commit.
|
||||
print("Downloading gyp...")
|
||||
run([
|
||||
"git", "clone", "--quiet", "--depth=1",
|
||||
"https://chromium.googlesource.com/external/gyp.git",
|
||||
LIB_UV_DIR + "/build/gyp"
|
||||
])
|
||||
|
||||
# We don't need all of libuv and gyp's various support files.
|
||||
print("Deleting unneeded files...")
|
||||
remove_dir("deps/libuv/build/gyp/buildbot")
|
||||
remove_dir("deps/libuv/build/gyp/infra")
|
||||
remove_dir("deps/libuv/build/gyp/samples")
|
||||
remove_dir("deps/libuv/build/gyp/test")
|
||||
remove_dir("deps/libuv/build/gyp/tools")
|
||||
remove_dir("deps/libuv/docs")
|
||||
remove_dir("deps/libuv/img")
|
||||
remove_dir("deps/libuv/samples")
|
||||
remove_dir("deps/libuv/test")
|
||||
|
||||
# We are going to commit libuv and GYP in the main Wren repo, so we don't
|
||||
# want them to be their own repos.
|
||||
remove_dir("deps/libuv/.git")
|
||||
remove_dir("deps/libuv/build/gyp/.git")
|
||||
|
||||
# Libuv's .gitignore ignores GYP, but we want to commit it.
|
||||
replace_in_file("deps/libuv/.gitignore",
|
||||
"/build/gyp",
|
||||
"# /build/gyp (We do want to commit GYP in Wren's repo)")
|
||||
|
||||
|
||||
main(sys.argv)
|
||||
77
util/util.py
77
util/util.py
@ -1,77 +0,0 @@
|
||||
# Utility functions used by other Python files in this directory.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def python2_binary():
|
||||
"""Tries to find a python 2 executable."""
|
||||
|
||||
# Using [0] instead of .major here to support Python 2.6.
|
||||
if sys.version_info[0] == 2:
|
||||
return sys.executable or "python"
|
||||
else:
|
||||
return "python2"
|
||||
|
||||
|
||||
def clean_dir(dir):
|
||||
"""If dir exists, deletes it and recreates it, otherwise creates it."""
|
||||
if os.path.isdir(dir):
|
||||
remove_dir(dir)
|
||||
|
||||
os.makedirs(dir)
|
||||
|
||||
|
||||
def ensure_dir(dir):
|
||||
"""Creates dir if not already there."""
|
||||
|
||||
if os.path.isdir(dir):
|
||||
return
|
||||
|
||||
os.makedirs(dir)
|
||||
|
||||
|
||||
def remove_dir(dir):
|
||||
"""Recursively removes dir."""
|
||||
|
||||
if platform.system() == "Windows":
|
||||
# rmtree gives up on readonly files on Windows
|
||||
# rd doesn't like paths with forward slashes
|
||||
subprocess.check_call(
|
||||
['cmd', '/c', 'rd', '/s', '/q', dir.replace('/', '\\')])
|
||||
else:
|
||||
shutil.rmtree(dir)
|
||||
|
||||
|
||||
def replace_in_file(path, text, replace):
|
||||
"""Replaces all occurrences of `text` in the file at `path` with `replace`."""
|
||||
with open(path) as file:
|
||||
contents = file.read()
|
||||
|
||||
contents = contents.replace(text, replace)
|
||||
|
||||
with open(path, "w") as file:
|
||||
file.write(contents)
|
||||
|
||||
|
||||
def run(args, cwd=None):
|
||||
"""Spawn a process to invoke [args] and mute its output."""
|
||||
|
||||
try:
|
||||
# check_output() was added in Python 2.7.
|
||||
has_check_output = (sys.version_info[0] > 2 or
|
||||
(sys.version_info[0] == 2 and sys.version_info[1] >= 7))
|
||||
|
||||
if has_check_output:
|
||||
subprocess.check_output(args, cwd=cwd, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
proc = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE)
|
||||
proc.communicate()[0].split()
|
||||
except subprocess.CalledProcessError as error:
|
||||
print(error.output)
|
||||
sys.exit(error.returncode)
|
||||
257
util/wren.mk
257
util/wren.mk
@ -1,257 +0,0 @@
|
||||
# Makefile for building a single configuration of Wren. It allows the
|
||||
# following variables to be passed to it:
|
||||
#
|
||||
# MODE - The build mode, "debug" or "release".
|
||||
# If omitted, defaults to "release".
|
||||
# LANG - The language, "c" or "cpp".
|
||||
# If omitted, defaults to "c".
|
||||
# ARCH - The processor architecture, "32", "64", or nothing, which indicates
|
||||
# the compiler's default.
|
||||
# If omitted, defaults to the compiler's default.
|
||||
#
|
||||
# It builds a static library, shared library, and command-line interpreter for
|
||||
# the given configuration. Libraries are built to "lib", and the interpreter
|
||||
# is built to "bin".
|
||||
#
|
||||
# The output file is initially "wren". If in debug mode, "d" is appended to it.
|
||||
# If the language is "cpp", then "-cpp" is appended to that. If the
|
||||
# architecture is not the default then "-32" or "-64" is appended to that.
|
||||
# Then, for the libraries, the correct extension is added.
|
||||
|
||||
# Files.
|
||||
OPT_HEADERS := $(wildcard src/optional/*.h) $(wildcard src/optional/*.wren.inc)
|
||||
OPT_SOURCES := $(wildcard src/optional/*.c)
|
||||
|
||||
CLI_HEADERS := $(wildcard src/cli/*.h)
|
||||
CLI_SOURCES := $(wildcard src/cli/*.c)
|
||||
|
||||
MODULE_HEADERS := $(wildcard src/module/*.h) $(wildcard src/module/*.wren.inc)
|
||||
MODULE_SOURCES := $(wildcard src/module/*.c)
|
||||
|
||||
VM_HEADERS := $(wildcard src/vm/*.h) $(wildcard src/vm/*.wren.inc)
|
||||
VM_SOURCES := $(wildcard src/vm/*.c)
|
||||
|
||||
API_TEST_HEADERS := $(wildcard test/api/*.h)
|
||||
API_TEST_SOURCES := $(wildcard test/api/*.c)
|
||||
|
||||
UNIT_TEST_HEADERS := $(wildcard test/unit/*.h)
|
||||
UNIT_TEST_SOURCES := $(wildcard test/unit/*.c)
|
||||
|
||||
BUILD_DIR := build
|
||||
|
||||
# Allows one to enable verbose builds with VERBOSE=1
|
||||
V := @
|
||||
ifeq ($(VERBOSE),1)
|
||||
V :=
|
||||
endif
|
||||
|
||||
C_OPTIONS := $(WREN_CFLAGS)
|
||||
C_WARNINGS := -Wall -Wextra -Werror -Wno-unused-parameter
|
||||
# Wren uses callbacks heavily, so -Wunused-parameter is too painful to enable.
|
||||
|
||||
# Mode configuration.
|
||||
ifeq ($(MODE),debug)
|
||||
WREN := wrend
|
||||
C_OPTIONS += -O0 -DDEBUG -g
|
||||
BUILD_DIR := $(BUILD_DIR)/debug
|
||||
else
|
||||
WREN += wren
|
||||
C_OPTIONS += -O3
|
||||
BUILD_DIR := $(BUILD_DIR)/release
|
||||
endif
|
||||
|
||||
# Language configuration.
|
||||
ifeq ($(LANG),cpp)
|
||||
WREN := $(WREN)-cpp
|
||||
C_OPTIONS += -std=c++98
|
||||
FILE_FLAG := -x c++
|
||||
BUILD_DIR := $(BUILD_DIR)-cpp
|
||||
else
|
||||
C_OPTIONS += -std=c99
|
||||
endif
|
||||
|
||||
# Architecture configuration.
|
||||
ifeq ($(ARCH),32)
|
||||
C_OPTIONS += -m32
|
||||
WREN := $(WREN)-32
|
||||
BUILD_DIR := $(BUILD_DIR)-32
|
||||
LIBUV_ARCH := -32
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),64)
|
||||
C_OPTIONS += -m64
|
||||
WREN := $(WREN)-64
|
||||
BUILD_DIR := $(BUILD_DIR)-64
|
||||
LIBUV_ARCH := -64
|
||||
endif
|
||||
|
||||
# Some platform-specific workarounds. Note that we use "gcc" explicitly in the
|
||||
# call to get the machine name because one of these workarounds deals with $(CC)
|
||||
# itself not working.
|
||||
OS := $(lastword $(subst -, ,$(shell gcc -dumpmachine)))
|
||||
|
||||
# Don't add -fPIC on Windows since it generates a warning which gets promoted
|
||||
# to an error by -Werror.
|
||||
ifeq ($(OS),mingw32)
|
||||
else ifeq ($(OS),cygwin)
|
||||
# Do nothing.
|
||||
else
|
||||
C_OPTIONS += -fPIC
|
||||
endif
|
||||
|
||||
# MinGW--or at least some versions of it--default CC to "cc" but then don't
|
||||
# provide an executable named "cc". Manually point to "gcc" instead.
|
||||
ifeq ($(OS),mingw32)
|
||||
CC = GCC
|
||||
endif
|
||||
|
||||
# Clang on Mac OS X has different flags and a different extension to build a
|
||||
# shared library.
|
||||
ifneq (,$(findstring darwin,$(OS)))
|
||||
SHARED_EXT := dylib
|
||||
else
|
||||
SHARED_LIB_FLAGS := -Wl,-soname,libwren.so
|
||||
SHARED_EXT := so
|
||||
|
||||
# Link in the right libraries needed by libuv on Windows and Linux.
|
||||
ifeq ($(OS),mingw32)
|
||||
LIBUV_LIBS := -lws2_32 -liphlpapi -lpsapi -luserenv
|
||||
else
|
||||
LIBUV_LIBS := -lpthread -lrt
|
||||
endif
|
||||
endif
|
||||
|
||||
CFLAGS := $(C_OPTIONS) $(C_WARNINGS)
|
||||
|
||||
OPT_OBJECTS := $(addprefix $(BUILD_DIR)/optional/, $(notdir $(OPT_SOURCES:.c=.o)))
|
||||
CLI_OBJECTS := $(addprefix $(BUILD_DIR)/cli/, $(notdir $(CLI_SOURCES:.c=.o)))
|
||||
MODULE_OBJECTS := $(addprefix $(BUILD_DIR)/module/, $(notdir $(MODULE_SOURCES:.c=.o)))
|
||||
VM_OBJECTS := $(addprefix $(BUILD_DIR)/vm/, $(notdir $(VM_SOURCES:.c=.o)))
|
||||
API_TEST_OBJECTS := $(patsubst test/api/%.c, $(BUILD_DIR)/test/api/%.o, $(API_TEST_SOURCES))
|
||||
UNIT_TEST_OBJECTS := $(patsubst test/unit/%.c, $(BUILD_DIR)/test/unit/%.o, $(UNIT_TEST_SOURCES))
|
||||
|
||||
LIBUV_DIR := deps/libuv
|
||||
LIBUV := build/libuv$(LIBUV_ARCH).a
|
||||
|
||||
# Flags needed to compile source files for the CLI, including the modules and
|
||||
# API tests.
|
||||
CLI_FLAGS := -D_XOPEN_SOURCE=600 -Isrc/include -I$(LIBUV_DIR)/include \
|
||||
-Isrc/cli -Isrc/module
|
||||
|
||||
# Targets ---------------------------------------------------------------------
|
||||
|
||||
# Builds the VM libraries and CLI interpreter.
|
||||
all: vm cli
|
||||
|
||||
# Builds just the VM libraries.
|
||||
vm: shared static
|
||||
|
||||
# Builds the shared VM library.
|
||||
shared: lib/lib$(WREN).$(SHARED_EXT)
|
||||
|
||||
# Builds the static VM library.
|
||||
static: lib/lib$(WREN).a
|
||||
|
||||
# Builds just the CLI interpreter.
|
||||
cli: bin/$(WREN)
|
||||
|
||||
# Builds the API test executable.
|
||||
api_test: $(BUILD_DIR)/test/api_$(WREN)
|
||||
|
||||
# Builds the unit test executable.
|
||||
unit_test: $(BUILD_DIR)/test/unit_$(WREN)
|
||||
|
||||
# Command-line interpreter.
|
||||
bin/$(WREN): $(OPT_OBJECTS) $(CLI_OBJECTS) $(MODULE_OBJECTS) $(VM_OBJECTS) \
|
||||
$(LIBUV)
|
||||
@ printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS)"
|
||||
$(V) mkdir -p bin
|
||||
$(V) $(CC) $(CFLAGS) $^ -o $@ -lm $(LIBUV_LIBS)
|
||||
|
||||
# Static library.
|
||||
lib/lib$(WREN).a: $(OPT_OBJECTS) $(VM_OBJECTS)
|
||||
@ printf "%10s %-30s %s\n" $(AR) $@ "rcu"
|
||||
$(V) mkdir -p lib
|
||||
$(V) $(AR) rcu $@ $^
|
||||
|
||||
# Shared library.
|
||||
lib/lib$(WREN).$(SHARED_EXT): $(OPT_OBJECTS) $(VM_OBJECTS)
|
||||
@ printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS) $(SHARED_LIB_FLAGS)"
|
||||
$(V) mkdir -p lib
|
||||
$(V) $(CC) $(CFLAGS) -shared $(SHARED_LIB_FLAGS) -o $@ $^
|
||||
|
||||
# API test executable.
|
||||
$(BUILD_DIR)/test/api_$(WREN): $(OPT_OBJECTS) $(MODULE_OBJECTS) $(API_TEST_OBJECTS) \
|
||||
$(VM_OBJECTS) $(BUILD_DIR)/cli/modules.o $(BUILD_DIR)/cli/vm.o \
|
||||
$(BUILD_DIR)/cli/path.o $(LIBUV)
|
||||
@ printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS)"
|
||||
$(V) mkdir -p $(BUILD_DIR)/test/api
|
||||
$(V) $(CC) $(CFLAGS) $^ -o $@ -lm $(LIBUV_LIBS)
|
||||
|
||||
# Unit test executable.
|
||||
$(BUILD_DIR)/test/unit_$(WREN): $(OPT_OBJECTS) $(MODULE_OBJECTS) $(UNIT_TEST_OBJECTS) \
|
||||
$(VM_OBJECTS) $(BUILD_DIR)/cli/modules.o $(BUILD_DIR)/cli/vm.o \
|
||||
$(BUILD_DIR)/cli/path.o $(LIBUV)
|
||||
@ printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS)"
|
||||
$(V) mkdir -p $(BUILD_DIR)/test/unit
|
||||
$(V) $(CC) $(CFLAGS) $^ -o $@
|
||||
|
||||
# CLI object files.
|
||||
$(BUILD_DIR)/cli/%.o: src/cli/%.c $(CLI_HEADERS) $(MODULE_HEADERS) \
|
||||
$(VM_HEADERS) $(LIBUV)
|
||||
@ printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
|
||||
$(V) mkdir -p $(BUILD_DIR)/cli
|
||||
$(V) $(CC) -c $(CFLAGS) $(CLI_FLAGS) -o $@ $(FILE_FLAG) $<
|
||||
|
||||
# Module object files.
|
||||
$(BUILD_DIR)/module/%.o: src/module/%.c $(CLI_HEADERS) $(MODULE_HEADERS) \
|
||||
$(VM_HEADERS) $(LIBUV)
|
||||
@ printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
|
||||
$(V) mkdir -p $(BUILD_DIR)/module
|
||||
$(V) $(CC) -c $(CFLAGS) $(CLI_FLAGS) -o $@ $(FILE_FLAG) $<
|
||||
|
||||
# Optional object files.
|
||||
$(BUILD_DIR)/optional/%.o: src/optional/%.c $(VM_HEADERS) $(OPT_HEADERS)
|
||||
@ printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
|
||||
$(V) mkdir -p $(BUILD_DIR)/optional
|
||||
$(V) $(CC) -c $(CFLAGS) -Isrc/include -Isrc/vm -o $@ $(FILE_FLAG) $<
|
||||
|
||||
# VM object files.
|
||||
$(BUILD_DIR)/vm/%.o: src/vm/%.c $(VM_HEADERS)
|
||||
@ printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
|
||||
$(V) mkdir -p $(BUILD_DIR)/vm
|
||||
$(V) $(CC) -c $(CFLAGS) -Isrc/include -Isrc/optional -Isrc/vm -o $@ $(FILE_FLAG) $<
|
||||
|
||||
# API test object files.
|
||||
$(BUILD_DIR)/test/api/%.o: test/api/%.c $(OPT_HEADERS) $(MODULE_HEADERS) \
|
||||
$(VM_HEADERS) $(API_TEST_HEADERS) $(LIBUV)
|
||||
@ printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
|
||||
$(V) mkdir -p $(dir $@)
|
||||
$(V) $(CC) -c $(CFLAGS) $(CLI_FLAGS) -o $@ $(FILE_FLAG) $<
|
||||
|
||||
# Unit test object files.
|
||||
$(BUILD_DIR)/test/unit/%.o: test/unit/%.c $(OPT_HEADERS) $(MODULE_HEADERS) \
|
||||
$(VM_HEADERS) $(UNIT_TEST_HEADERS) $(LIBUV)
|
||||
@ printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
|
||||
$(V) mkdir -p $(dir $@)
|
||||
$(V) $(CC) -c $(CFLAGS) $(CLI_FLAGS) -o $@ $(FILE_FLAG) $<
|
||||
|
||||
# Build libuv to a static library.
|
||||
$(LIBUV):
|
||||
@ printf "%10s %-30s %s\n" run util/build_libuv.py
|
||||
$(V) ./util/build_libuv.py $(LIBUV_ARCH)
|
||||
|
||||
# Wren modules that get compiled into the binary as C strings.
|
||||
src/optional/wren_opt_%.wren.inc: src/optional/wren_opt_%.wren util/wren_to_c_string.py
|
||||
@ printf "%10s %-30s %s\n" str $<
|
||||
$(V) ./util/wren_to_c_string.py $@ $<
|
||||
|
||||
src/vm/wren_%.wren.inc: src/vm/wren_%.wren util/wren_to_c_string.py
|
||||
@ printf "%10s %-30s %s\n" str $<
|
||||
$(V) ./util/wren_to_c_string.py $@ $<
|
||||
|
||||
src/module/%.wren.inc: src/module/%.wren util/wren_to_c_string.py
|
||||
@ printf "%10s %-30s %s\n" str $<
|
||||
$(V) ./util/wren_to_c_string.py $@ $<
|
||||
|
||||
.PHONY: all api_test cli unit_test vm
|
||||
Loading…
Reference in New Issue
Block a user