Merge branch 'master' into smarter-imports
# Conflicts: # src/module/io.c # src/vm/wren_vm.c
This commit is contained in:
commit
7a42a20b98
25
.travis.yml
25
.travis.yml
@ -1,13 +1,22 @@
|
||||
language: c
|
||||
os:
|
||||
- osx
|
||||
- linux
|
||||
- osx
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
env:
|
||||
- WREN_OPTIONS="" CI_ARCHS="ci_32 ci_64"
|
||||
- WREN_OPTIONS="-DWREN_NAN_TAGGING=0" CI_ARCHS="ci_64"
|
||||
|
||||
# Automatically build and deploy docs.
|
||||
jobs:
|
||||
include:
|
||||
- stage: deploy
|
||||
script: bash util/deploy_docs_from_travis.sh
|
||||
# Only deploy commits that land on master.
|
||||
if: branch = master and type = push
|
||||
|
||||
# Travis VMs are 64-bit but we compile both for 32 and 64 bit. To enable the
|
||||
# 32-bit builds to work, we need gcc-multilib.
|
||||
addons:
|
||||
@ -15,5 +24,17 @@ addons:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- g++-multilib
|
||||
sudo: false # Enable container-based builds.
|
||||
# These are needed for building and deploying the docs.
|
||||
- python3-markdown
|
||||
- python3-pygments
|
||||
- python3-setuptools
|
||||
- ruby-sass
|
||||
|
||||
# Can't do container-based builds for now because installing the custom
|
||||
# Pygments lexer to generate the docs requires sudo. :( If that changes,
|
||||
# uncomment the next line and delete the "sudo" and "dist" lines.
|
||||
# sudo: false # Enable container-based builds.
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
script: make WREN_CFLAGS=${WREN_OPTIONS} ${CI_ARCHS}
|
||||
|
||||
3
AUTHORS
3
AUTHORS
@ -20,3 +20,6 @@ Damien Radtke <damienradtke@gmail.com>
|
||||
Max Ferguson <maxxferguson@gmail.com>
|
||||
Sven Bergström <sven@underscorediscovery.com>
|
||||
Kyle Charters <kylewcharters@gmail.com>
|
||||
Marshall Bowers <elliott.codes@gmail.com>
|
||||
Michal Kozakiewicz <michalkozakiewicz3@gmail.com>
|
||||
Charlotte Koch <cfkoch@edgebsd.org>
|
||||
|
||||
24
LICENSE
24
LICENSE
@ -1,23 +1,21 @@
|
||||
Wren uses the MIT License:
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-2016 Robert Nystrom
|
||||
Copyright (c) 2013 Robert Nystrom
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software. (As clarification, there is no
|
||||
requirement that the copyright notice and permission be included in binary
|
||||
distributions of the Software.)
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
1
Makefile
1
Makefile
@ -90,6 +90,7 @@ unit_test:
|
||||
|
||||
# Generate the Wren site.
|
||||
docs:
|
||||
mkdir -p build
|
||||
$(V) ./util/generate_docs.py
|
||||
|
||||
# Continuously generate and serve the Wren site.
|
||||
|
||||
@ -158,9 +158,10 @@ are available on an object. When you write:
|
||||
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.
|
||||
`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`
|
||||
|
||||
@ -430,7 +431,7 @@ They can be used from static methods:
|
||||
|
||||
:::wren
|
||||
Foo.setFromStatic("first")
|
||||
Foo.bar.printFromStatic() //> 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:
|
||||
|
||||
@ -148,14 +148,14 @@ method calls — the entire callstack — gets suspended. For example:
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {
|
||||
(1..10).map {|i|
|
||||
(1..10).each {|i|
|
||||
Fiber.yield(i)
|
||||
}
|
||||
}
|
||||
|
||||
Here, we're calling `yield()` from within a [function](functions.html) being
|
||||
passed to the `map()` method. This works fine in Wren because that inner
|
||||
`yield()` call will suspend the call to `map()` and the function passed to it
|
||||
passed to the `each()` method. This works fine in Wren because that inner
|
||||
`yield()` call will suspend the call to `each()` and the function passed to it
|
||||
as a callback.
|
||||
|
||||
## Transferring control
|
||||
|
||||
@ -66,7 +66,7 @@ for Wren. To install that, run:
|
||||
|
||||
:::sh
|
||||
$ cd util/pygments-lexer
|
||||
$ sudo python setup.py develop
|
||||
$ sudo python3 setup.py develop
|
||||
$ cd ../.. # Back to the root Wren directory.
|
||||
|
||||
Now you can build the docs:
|
||||
@ -79,7 +79,7 @@ server from there. Python includes one:
|
||||
|
||||
:::sh
|
||||
$ cd build/docs
|
||||
$ python -m SimpleHTTPServer
|
||||
$ 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
|
||||
|
||||
@ -76,7 +76,7 @@ Something like:
|
||||
{
|
||||
if (strcmp(className, "Math") == 0)
|
||||
{
|
||||
if (!isStatic && strcmp(signature, "add(_,_)") == 0)
|
||||
if (isStatic && strcmp(signature, "add(_,_)") == 0)
|
||||
{
|
||||
return mathAdd; // C function for Math.add(_,_).
|
||||
}
|
||||
|
||||
@ -102,6 +102,7 @@ is:
|
||||
|
||||
:::c
|
||||
void error(
|
||||
WrenVM* vm,
|
||||
WrenErrorType type,
|
||||
const char* module,
|
||||
int line,
|
||||
|
||||
@ -270,7 +270,7 @@ and closes the file:
|
||||
:::c
|
||||
void fileFinalize(void* data)
|
||||
{
|
||||
closeFile((FILE**)file);
|
||||
closeFile((FILE**) data);
|
||||
}
|
||||
|
||||
It uses this little utility function:
|
||||
|
||||
@ -129,28 +129,12 @@ 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.
|
||||
|
||||
### **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.
|
||||
|
||||
### **replace**(old, swap)
|
||||
|
||||
Returns a new string with all occurences of `old` replaced with `swap`.
|
||||
|
||||
:::wren
|
||||
var string = "abc abc abc"
|
||||
System.print(string.replace(" ", "")) //> abcabcabc
|
||||
|
||||
### **iterate**(iterator), **iteratorValue**(iterator)
|
||||
|
||||
Implements the [iterator protocol](../../control-flow.html#the-iterator-protocol)
|
||||
for iterating over the *code points* in the string:
|
||||
Implements the [iterator protocol][] for iterating over the *code points* in the
|
||||
string:
|
||||
|
||||
[iterator protocol]: ../../control-flow.html#the-iterator-protocol
|
||||
|
||||
:::wren
|
||||
var codePoints = []
|
||||
@ -163,12 +147,74 @@ for iterating over the *code points* in the string:
|
||||
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`.
|
||||
|
||||
@ -97,7 +97,7 @@ The size of the contents of the file in bytes.
|
||||
|
||||
### **close**()
|
||||
|
||||
Closes the file. After calling this, you can read or write from it.
|
||||
Closes the file. After calling this, you can't read or write from it.
|
||||
|
||||
### **readBytes**(count)
|
||||
|
||||
|
||||
@ -192,6 +192,7 @@ static ModuleRegistry modules[] =
|
||||
#undef END_CLASS
|
||||
#undef METHOD
|
||||
#undef STATIC_METHOD
|
||||
#undef FINALIZER
|
||||
|
||||
// Looks for a built-in module with [name].
|
||||
//
|
||||
@ -241,7 +242,7 @@ char* readBuiltInModule(const char* name)
|
||||
|
||||
size_t length = strlen(*module->source);
|
||||
char* copy = (char*)malloc(length + 1);
|
||||
strncpy(copy, *module->source, length + 1);
|
||||
memcpy(copy, *module->source, length + 1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
@ -9,11 +9,21 @@
|
||||
#include <sys\stat.h>
|
||||
|
||||
// Map to Windows permission flags.
|
||||
#ifndef S_IRUSR
|
||||
#define S_IRUSR _S_IREAD
|
||||
#define S_IWUSR _S_IWRITE
|
||||
#endif
|
||||
|
||||
#ifndef S_IWUSR
|
||||
#define S_IWUSR _S_IWRITE
|
||||
#endif
|
||||
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
// Not supported on Windows.
|
||||
#define O_SYNC 0
|
||||
|
||||
@ -11,11 +11,6 @@ class Repl {
|
||||
|
||||
_history = []
|
||||
_historyIndex = 0
|
||||
|
||||
// Whether or not we allow ASCI escape sequences for showing colors and
|
||||
// moving the cursor. When this is false, the REPL has reduced
|
||||
// functionality.
|
||||
_allowAnsi = false
|
||||
}
|
||||
|
||||
cursor { _cursor }
|
||||
@ -78,7 +73,7 @@ class Repl {
|
||||
insertChar(byte)
|
||||
} else {
|
||||
// TODO: Other shortcuts?
|
||||
System.print("Unhandled byte: %(byte)")
|
||||
System.print("Unhandled key-code [dec]: %(byte)")
|
||||
}
|
||||
|
||||
return false
|
||||
@ -172,15 +167,17 @@ class Repl {
|
||||
token.type == Token.varKeyword ||
|
||||
token.type == Token.whileKeyword
|
||||
|
||||
var fiber
|
||||
var closure
|
||||
if (isStatement) {
|
||||
fiber = Meta.compile(input)
|
||||
closure = Meta.compile(input)
|
||||
} else {
|
||||
fiber = Meta.compileExpression(input)
|
||||
closure = Meta.compileExpression(input)
|
||||
}
|
||||
|
||||
// Stop if there was a compile error.
|
||||
if (fiber == null) return
|
||||
if (closure == null) return
|
||||
|
||||
var fiber = Fiber.new(closure)
|
||||
|
||||
var result = fiber.try()
|
||||
if (fiber.error != null) {
|
||||
|
||||
@ -13,11 +13,6 @@ static const char* replModuleSource =
|
||||
"\n"
|
||||
" _history = []\n"
|
||||
" _historyIndex = 0\n"
|
||||
"\n"
|
||||
" // Whether or not we allow ASCI escape sequences for showing colors and\n"
|
||||
" // moving the cursor. When this is false, the REPL has reduced\n"
|
||||
" // functionality.\n"
|
||||
" _allowAnsi = false\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" cursor { _cursor }\n"
|
||||
@ -80,7 +75,7 @@ static const char* replModuleSource =
|
||||
" insertChar(byte)\n"
|
||||
" } else {\n"
|
||||
" // TODO: Other shortcuts?\n"
|
||||
" System.print(\"Unhandled byte: %(byte)\")\n"
|
||||
" System.print(\"Unhandled key-code [dec]: %(byte)\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return false\n"
|
||||
@ -174,15 +169,17 @@ static const char* replModuleSource =
|
||||
" token.type == Token.varKeyword ||\n"
|
||||
" token.type == Token.whileKeyword\n"
|
||||
"\n"
|
||||
" var fiber\n"
|
||||
" var closure\n"
|
||||
" if (isStatement) {\n"
|
||||
" fiber = Meta.compile(input)\n"
|
||||
" closure = Meta.compile(input)\n"
|
||||
" } else {\n"
|
||||
" fiber = Meta.compileExpression(input)\n"
|
||||
" closure = Meta.compileExpression(input)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" // Stop if there was a compile error.\n"
|
||||
" if (fiber == null) return\n"
|
||||
" if (closure == null) return\n"
|
||||
"\n"
|
||||
" var fiber = Fiber.new(closure)\n"
|
||||
"\n"
|
||||
" var result = fiber.try()\n"
|
||||
" if (fiber.error != null) {\n"
|
||||
|
||||
@ -61,9 +61,7 @@ void metaGetModuleVariables(WrenVM* vm)
|
||||
|
||||
for (int i = 0; i < names->elements.count; i++)
|
||||
{
|
||||
String* name = &module->variableNames.data[i];
|
||||
names->elements.data[i] = wrenNewStringLength(vm,
|
||||
name->buffer, name->length);
|
||||
names->elements.data[i] = OBJ_VAL(module->variableNames.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
// is kind of hairy, but fortunately we can control what the longest possible
|
||||
// message is and handle that. Ideally, we'd use `snprintf()`, but that's not
|
||||
// available in standard C++98.
|
||||
#define ERROR_MESSAGE_SIZE (60 + MAX_VARIABLE_NAME + 15)
|
||||
#define ERROR_MESSAGE_SIZE (80 + MAX_VARIABLE_NAME + 15)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@ -498,7 +498,7 @@ static int addConstant(Compiler* compiler, Value constant)
|
||||
|
||||
// Initializes [compiler].
|
||||
static void initCompiler(Compiler* compiler, Parser* parser, Compiler* parent,
|
||||
bool isFunction)
|
||||
bool isMethod)
|
||||
{
|
||||
compiler->parser = parser;
|
||||
compiler->parent = parent;
|
||||
@ -512,41 +512,41 @@ static void initCompiler(Compiler* compiler, Parser* parser, Compiler* parent,
|
||||
|
||||
parser->vm->compiler = compiler;
|
||||
|
||||
// Declare a local slot for either the closure or method receiver so that we
|
||||
// don't try to reuse that slot for a user-defined local variable. For
|
||||
// methods, we name it "this", so that we can resolve references to that like
|
||||
// a normal variable. For functions, they have no explicit "this", so we use
|
||||
// an empty name. That way references to "this" inside a function walks up
|
||||
// the parent chain to find a method enclosing the function whose "this" we
|
||||
// can close over.
|
||||
compiler->numLocals = 1;
|
||||
compiler->numSlots = compiler->numLocals;
|
||||
|
||||
if (isMethod)
|
||||
{
|
||||
compiler->locals[0].name = "this";
|
||||
compiler->locals[0].length = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
compiler->locals[0].name = NULL;
|
||||
compiler->locals[0].length = 0;
|
||||
}
|
||||
|
||||
compiler->locals[0].depth = -1;
|
||||
compiler->locals[0].isUpvalue = false;
|
||||
|
||||
if (parent == NULL)
|
||||
{
|
||||
compiler->numLocals = 0;
|
||||
|
||||
// Compiling top-level code, so the initial scope is module-level.
|
||||
compiler->scopeDepth = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Declare a fake local variable for the receiver so that it's slot in the
|
||||
// stack is taken. For methods, we call this "this", so that we can resolve
|
||||
// references to that like a normal variable. For functions, they have no
|
||||
// explicit "this". So we pick a bogus name. That way references to "this"
|
||||
// inside a function will try to walk up the parent chain to find a method
|
||||
// enclosing the function whose "this" we can close over.
|
||||
compiler->numLocals = 1;
|
||||
if (isFunction)
|
||||
{
|
||||
compiler->locals[0].name = NULL;
|
||||
compiler->locals[0].length = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
compiler->locals[0].name = "this";
|
||||
compiler->locals[0].length = 4;
|
||||
}
|
||||
compiler->locals[0].depth = -1;
|
||||
compiler->locals[0].isUpvalue = false;
|
||||
|
||||
// The initial scope for function or method is a local scope.
|
||||
// The initial scope for functions and methods is local scope.
|
||||
compiler->scopeDepth = 0;
|
||||
}
|
||||
|
||||
compiler->numSlots = compiler->numLocals;
|
||||
|
||||
compiler->fn = wrenNewFunction(parser->vm, parser->module,
|
||||
compiler->numLocals);
|
||||
}
|
||||
@ -1700,7 +1700,11 @@ static void signatureParameterList(char name[MAX_METHOD_SIGNATURE], int* length,
|
||||
int numParams, char leftBracket, char rightBracket)
|
||||
{
|
||||
name[(*length)++] = leftBracket;
|
||||
for (int i = 0; i < numParams; i++)
|
||||
|
||||
// This function may be called with too many parameters. When that happens,
|
||||
// a compile error has already been reported, but we need to make sure we
|
||||
// don't overflow the string too, hence the MAX_PARAMETERS check.
|
||||
for (int i = 0; i < numParams && i < MAX_PARAMETERS; i++)
|
||||
{
|
||||
if (i > 0) name[(*length)++] = ',';
|
||||
name[(*length)++] = '_';
|
||||
@ -1863,7 +1867,7 @@ static void methodCall(Compiler* compiler, Code instruction,
|
||||
called.arity++;
|
||||
|
||||
Compiler fnCompiler;
|
||||
initCompiler(&fnCompiler, compiler->parser, compiler, true);
|
||||
initCompiler(&fnCompiler, compiler->parser, compiler, false);
|
||||
|
||||
// Make a dummy signature to track the arity.
|
||||
Signature fnSignature = { "", 0, SIG_METHOD, 0 };
|
||||
@ -2727,7 +2731,6 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
case CODE_LOAD_FIELD:
|
||||
case CODE_STORE_FIELD:
|
||||
case CODE_CLASS:
|
||||
case CODE_IMPORT_MODULE:
|
||||
return 1;
|
||||
|
||||
case CODE_CONSTANT:
|
||||
@ -2757,7 +2760,7 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
case CODE_OR:
|
||||
case CODE_METHOD_INSTANCE:
|
||||
case CODE_METHOD_STATIC:
|
||||
case CODE_IMPORT_VARIABLE:
|
||||
case CODE_IMPORT_MODULE:
|
||||
return 2;
|
||||
|
||||
case CODE_SUPER_0:
|
||||
@ -2777,6 +2780,7 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
case CODE_SUPER_14:
|
||||
case CODE_SUPER_15:
|
||||
case CODE_SUPER_16:
|
||||
case CODE_IMPORT_VARIABLE:
|
||||
return 4;
|
||||
|
||||
case CODE_CLOSURE:
|
||||
@ -3077,7 +3081,7 @@ static void createConstructor(Compiler* compiler, Signature* signature,
|
||||
int initializerSymbol)
|
||||
{
|
||||
Compiler methodCompiler;
|
||||
initCompiler(&methodCompiler, compiler->parser, compiler, false);
|
||||
initCompiler(&methodCompiler, compiler->parser, compiler, true);
|
||||
|
||||
// Allocate the instance.
|
||||
emitOp(&methodCompiler, compiler->enclosingClass->isForeign
|
||||
@ -3163,7 +3167,7 @@ static bool method(Compiler* compiler, Variable classVariable)
|
||||
compiler->enclosingClass->signature = &signature;
|
||||
|
||||
Compiler methodCompiler;
|
||||
initCompiler(&methodCompiler, compiler->parser, compiler, false);
|
||||
initCompiler(&methodCompiler, compiler->parser, compiler, true);
|
||||
|
||||
// Compile the method signature.
|
||||
signatureFn(&methodCompiler, &signature);
|
||||
@ -3416,6 +3420,9 @@ void definition(Compiler* compiler)
|
||||
ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* source,
|
||||
bool isExpression, bool printErrors)
|
||||
{
|
||||
// Skip the UTF-8 BOM if there is one.
|
||||
if (strncmp(source, "\xEF\xBB\xBF", 3) == 0) source += 3;
|
||||
|
||||
Parser parser;
|
||||
parser.vm = vm;
|
||||
parser.module = module;
|
||||
@ -3443,14 +3450,15 @@ ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* source,
|
||||
nextToken(&parser);
|
||||
|
||||
int numExistingVariables = module->variables.count;
|
||||
|
||||
|
||||
Compiler compiler;
|
||||
initCompiler(&compiler, &parser, NULL, true);
|
||||
initCompiler(&compiler, &parser, NULL, false);
|
||||
ignoreNewlines(&compiler);
|
||||
|
||||
if (isExpression)
|
||||
{
|
||||
expression(&compiler);
|
||||
consume(&compiler, TOKEN_EOF, "Expect end of expression.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3458,7 +3466,7 @@ ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* source,
|
||||
{
|
||||
definition(&compiler);
|
||||
|
||||
// If there is no newline, it must be the end of the block on the same line.
|
||||
// If there is no newline, it must be the end of file on the same line.
|
||||
if (!matchLine(&compiler))
|
||||
{
|
||||
consume(&compiler, TOKEN_EOF, "Expect end of file.");
|
||||
@ -3480,8 +3488,8 @@ ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* source,
|
||||
{
|
||||
// Synthesize a token for the original use site.
|
||||
parser.previous.type = TOKEN_NAME;
|
||||
parser.previous.start = parser.module->variableNames.data[i].buffer;
|
||||
parser.previous.length = parser.module->variableNames.data[i].length;
|
||||
parser.previous.start = parser.module->variableNames.data[i]->value;
|
||||
parser.previous.length = parser.module->variableNames.data[i]->length;
|
||||
parser.previous.line = (int)AS_NUM(parser.module->variables.data[i]);
|
||||
error(&compiler, "Variable is used but not defined.");
|
||||
}
|
||||
@ -3495,7 +3503,7 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
|
||||
int ip = 0;
|
||||
for (;;)
|
||||
{
|
||||
Code instruction = (Code)fn->code.data[ip++];
|
||||
Code instruction = (Code)fn->code.data[ip];
|
||||
switch (instruction)
|
||||
{
|
||||
case CODE_LOAD_FIELD:
|
||||
@ -3505,7 +3513,7 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
|
||||
// Shift this class's fields down past the inherited ones. We don't
|
||||
// check for overflow here because we'll see if the number of fields
|
||||
// overflows when the subclass is created.
|
||||
fn->code.data[ip++] += classObj->superclass->numFields;
|
||||
fn->code.data[ip + 1] += classObj->superclass->numFields;
|
||||
break;
|
||||
|
||||
case CODE_SUPER_0:
|
||||
@ -3526,11 +3534,8 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
|
||||
case CODE_SUPER_15:
|
||||
case CODE_SUPER_16:
|
||||
{
|
||||
// Skip over the symbol.
|
||||
ip += 2;
|
||||
|
||||
// Fill in the constant slot with a reference to the superclass.
|
||||
int constant = (fn->code.data[ip] << 8) | fn->code.data[ip + 1];
|
||||
int constant = (fn->code.data[ip + 3] << 8) | fn->code.data[ip + 4];
|
||||
fn->constants.data[constant] = OBJ_VAL(classObj->superclass);
|
||||
break;
|
||||
}
|
||||
@ -3538,10 +3543,8 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
|
||||
case CODE_CLOSURE:
|
||||
{
|
||||
// Bind the nested closure too.
|
||||
int constant = (fn->code.data[ip] << 8) | fn->code.data[ip + 1];
|
||||
int constant = (fn->code.data[ip + 1] << 8) | fn->code.data[ip + 2];
|
||||
wrenBindMethodCode(classObj, AS_FN(fn->constants.data[constant]));
|
||||
|
||||
ip += getNumArguments(fn->code.data, fn->constants.data, ip - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3550,9 +3553,9 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
|
||||
|
||||
default:
|
||||
// Other instructions are unaffected, so just skip over them.
|
||||
ip += getNumArguments(fn->code.data, fn->constants.data, ip - 1);
|
||||
break;
|
||||
}
|
||||
ip += 1 + getNumArguments(fn->code.data, fn->constants.data, ip);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3567,6 +3570,12 @@ void wrenMarkCompiler(WrenVM* vm, Compiler* compiler)
|
||||
{
|
||||
wrenGrayObj(vm, (Obj*)compiler->fn);
|
||||
wrenGrayObj(vm, (Obj*)compiler->constants);
|
||||
|
||||
if (compiler->enclosingClass != NULL)
|
||||
{
|
||||
wrenBlackenSymbolTable(vm, &compiler->enclosingClass->fields);
|
||||
}
|
||||
|
||||
compiler = compiler->parent;
|
||||
}
|
||||
while (compiler != NULL);
|
||||
|
||||
@ -59,15 +59,7 @@ DEF_PRIMITIVE(fiber_new)
|
||||
RETURN_ERROR("Function cannot take more than one parameter.");
|
||||
}
|
||||
|
||||
ObjFiber* newFiber = wrenNewFiber(vm, closure);
|
||||
|
||||
// The compiler expects the first slot of a function to hold the receiver.
|
||||
// Since a fiber's stack is invoked directly, it doesn't have one, so put it
|
||||
// in here.
|
||||
newFiber->stack[0] = NULL_VAL;
|
||||
newFiber->stackTop++;
|
||||
|
||||
RETURN_OBJ(newFiber);
|
||||
RETURN_OBJ(wrenNewFiber(vm, closure));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fiber_abort)
|
||||
|
||||
@ -236,6 +236,48 @@ class String is Sequence {
|
||||
return result
|
||||
}
|
||||
|
||||
trim() { trim_("\t\r\n ", true, true) }
|
||||
trim(chars) { trim_(chars, true, true) }
|
||||
trimEnd() { trim_("\t\r\n ", false, true) }
|
||||
trimEnd(chars) { trim_(chars, false, true) }
|
||||
trimStart() { trim_("\t\r\n ", true, false) }
|
||||
trimStart(chars) { trim_(chars, true, false) }
|
||||
|
||||
trim_(chars, trimStart, trimEnd) {
|
||||
if (!(chars is String)) {
|
||||
Fiber.abort("Characters must be a string.")
|
||||
}
|
||||
|
||||
var codePoints = chars.codePoints.toList
|
||||
|
||||
var start
|
||||
if (trimStart) {
|
||||
while (start = iterate(start)) {
|
||||
if (!codePoints.contains(codePointAt_(start))) break
|
||||
}
|
||||
|
||||
if (start == false) return ""
|
||||
} else {
|
||||
start = 0
|
||||
}
|
||||
|
||||
var end
|
||||
if (trimEnd) {
|
||||
end = byteCount_ - 1
|
||||
while (end >= start) {
|
||||
var codePoint = codePointAt_(end)
|
||||
if (codePoint != -1 && !codePoints.contains(codePoint)) break
|
||||
end = end - 1
|
||||
}
|
||||
|
||||
if (end < start) return ""
|
||||
} else {
|
||||
end = -1
|
||||
}
|
||||
|
||||
return this[start..end]
|
||||
}
|
||||
|
||||
*(count) {
|
||||
if (!(count is Num) || !count.isInteger || count < 0) {
|
||||
Fiber.abort("Count must be a non-negative integer.")
|
||||
|
||||
@ -238,6 +238,56 @@ static const char* coreModuleSource =
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" trim() { trim_(\"\t\r\n \", true, true) }\n"
|
||||
"\n"
|
||||
" trim(chars) { trim_(chars, true, true) }\n"
|
||||
"\n"
|
||||
" trimEnd() { trim_(\"\t\r\n \", false, true) }\n"
|
||||
"\n"
|
||||
" trimEnd(chars) { trim_(chars, false, true) }\n"
|
||||
"\n"
|
||||
" trimStart() { trim_(\"\t\r\n \", true, false) }\n"
|
||||
"\n"
|
||||
" trimStart(chars) { trim_(chars, true, false) }\n"
|
||||
"\n"
|
||||
" trim_(chars, trimStart, trimEnd) {\n"
|
||||
" if (!(chars is String)) {\n"
|
||||
" Fiber.abort(\"Characters must be a string.\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var codePoints = chars.codePoints.toList\n"
|
||||
"// System.print(\"code points %(codePoints)\")\n"
|
||||
"\n"
|
||||
" var start\n"
|
||||
" if (trimStart) {\n"
|
||||
" while (start = iterate(start)) {\n"
|
||||
" if (!codePoints.contains(codePointAt_(start))) break\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" if (start == false) return \"\"\n"
|
||||
" } else {\n"
|
||||
" start = 0\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var end\n"
|
||||
" if (trimEnd) {\n"
|
||||
" end = byteCount_ - 1\n"
|
||||
" while (end >= start) {\n"
|
||||
" var codePoint = codePointAt_(end)\n"
|
||||
"// System.print(\"test %(end) : %(codePoint)\")\n"
|
||||
" if (codePoint != -1 && !codePoints.contains(codePoint)) break\n"
|
||||
" end = end - 1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
"// System.print(\"range %(start) %(end)\")\n"
|
||||
" if (end < start) return \"\"\n"
|
||||
" } else {\n"
|
||||
" end = -1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return this[start..end]\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" *(count) {\n"
|
||||
" if (!(count is Num) || !count.isInteger || count < 0) {\n"
|
||||
" Fiber.abort(\"Count must be a non-negative integer.\")\n"
|
||||
|
||||
@ -159,7 +159,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
{
|
||||
int slot = READ_SHORT();
|
||||
printf("%-16s %5d '%s'\n", "LOAD_MODULE_VAR", slot,
|
||||
fn->module->variableNames.data[slot].buffer);
|
||||
fn->module->variableNames.data[slot]->value);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
{
|
||||
int slot = READ_SHORT();
|
||||
printf("%-16s %5d '%s'\n", "STORE_MODULE_VAR", slot,
|
||||
fn->module->variableNames.data[slot].buffer);
|
||||
fn->module->variableNames.data[slot]->value);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -199,7 +199,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
int numArgs = bytecode[i - 1] - CODE_CALL_0;
|
||||
int symbol = READ_SHORT();
|
||||
printf("CALL_%-11d %5d '%s'\n", numArgs, symbol,
|
||||
vm->methodNames.data[symbol].buffer);
|
||||
vm->methodNames.data[symbol]->value);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -225,7 +225,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
int symbol = READ_SHORT();
|
||||
int superclass = READ_SHORT();
|
||||
printf("SUPER_%-10d %5d '%s' %5d\n", numArgs, symbol,
|
||||
vm->methodNames.data[symbol].buffer, superclass);
|
||||
vm->methodNames.data[symbol]->value, superclass);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -301,7 +301,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
{
|
||||
int symbol = READ_SHORT();
|
||||
printf("%-16s %5d '%s'\n", "METHOD_INSTANCE", symbol,
|
||||
vm->methodNames.data[symbol].buffer);
|
||||
vm->methodNames.data[symbol]->value);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -309,7 +309,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
{
|
||||
int symbol = READ_SHORT();
|
||||
printf("%-16s %5d '%s'\n", "METHOD_STATIC", symbol,
|
||||
vm->methodNames.data[symbol].buffer);
|
||||
vm->methodNames.data[symbol]->value);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
DEFINE_BUFFER(Byte, uint8_t);
|
||||
DEFINE_BUFFER(Int, int);
|
||||
DEFINE_BUFFER(String, String);
|
||||
DEFINE_BUFFER(String, ObjString*);
|
||||
|
||||
void wrenSymbolTableInit(SymbolTable* symbols)
|
||||
{
|
||||
@ -14,24 +14,18 @@ void wrenSymbolTableInit(SymbolTable* symbols)
|
||||
|
||||
void wrenSymbolTableClear(WrenVM* vm, SymbolTable* symbols)
|
||||
{
|
||||
for (int i = 0; i < symbols->count; i++)
|
||||
{
|
||||
DEALLOCATE(vm, symbols->data[i].buffer);
|
||||
}
|
||||
|
||||
wrenStringBufferClear(vm, symbols);
|
||||
}
|
||||
|
||||
int wrenSymbolTableAdd(WrenVM* vm, SymbolTable* symbols,
|
||||
const char* name, size_t length)
|
||||
{
|
||||
String symbol;
|
||||
symbol.buffer = ALLOCATE_ARRAY(vm, char, length + 1);
|
||||
memcpy(symbol.buffer, name, length);
|
||||
symbol.buffer[length] = '\0';
|
||||
symbol.length = (int)length;
|
||||
|
||||
ObjString* symbol = AS_STRING(wrenNewStringLength(vm, name, length));
|
||||
|
||||
wrenPushRoot(vm, &symbol->obj);
|
||||
wrenStringBufferWrite(vm, symbols, symbol);
|
||||
wrenPopRoot(vm);
|
||||
|
||||
return symbols->count - 1;
|
||||
}
|
||||
|
||||
@ -46,20 +40,30 @@ int wrenSymbolTableEnsure(WrenVM* vm, SymbolTable* symbols,
|
||||
return wrenSymbolTableAdd(vm, symbols, name, length);
|
||||
}
|
||||
|
||||
int wrenSymbolTableFind(const SymbolTable* symbols,
|
||||
const char* name, size_t length)
|
||||
int wrenSymbolTableFind(const SymbolTable* symbols,
|
||||
const char* name, size_t length)
|
||||
{
|
||||
// See if the symbol is already defined.
|
||||
// TODO: O(n). Do something better.
|
||||
for (int i = 0; i < symbols->count; i++)
|
||||
{
|
||||
if (symbols->data[i].length == length &&
|
||||
memcmp(symbols->data[i].buffer, name, length) == 0) return i;
|
||||
if (wrenStringEqualsCString(symbols->data[i], name, length)) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void wrenBlackenSymbolTable(WrenVM* vm, SymbolTable* symbolTable)
|
||||
{
|
||||
for (int i = 0; i < symbolTable->count; i++)
|
||||
{
|
||||
wrenGrayObj(vm, &symbolTable->data[i]->obj);
|
||||
}
|
||||
|
||||
// Keep track of how much memory is still in use.
|
||||
vm->bytesAllocated += symbolTable->capacity * sizeof(*symbolTable->data);
|
||||
}
|
||||
|
||||
int wrenUtf8EncodeNumBytes(int value)
|
||||
{
|
||||
ASSERT(value >= 0, "Cannot encode a negative value.");
|
||||
|
||||
@ -6,12 +6,9 @@
|
||||
|
||||
// Reusable data structures and other utility functions.
|
||||
|
||||
// A simple structure to keep track of a string and its length (including the
|
||||
// null-terminator).
|
||||
typedef struct {
|
||||
char* buffer;
|
||||
uint32_t length;
|
||||
} String;
|
||||
// Forward declare this here to break a cycle between wren_utils.h and
|
||||
// wren_value.h.
|
||||
typedef struct sObjString ObjString;
|
||||
|
||||
// We need buffers of a few different types. To avoid lots of casting between
|
||||
// void* and back, we'll use the preprocessor as a poor man's generics and let
|
||||
@ -68,7 +65,7 @@ typedef struct {
|
||||
|
||||
DECLARE_BUFFER(Byte, uint8_t);
|
||||
DECLARE_BUFFER(Int, int);
|
||||
DECLARE_BUFFER(String, String);
|
||||
DECLARE_BUFFER(String, ObjString*);
|
||||
|
||||
// TODO: Change this to use a map.
|
||||
typedef StringBuffer SymbolTable;
|
||||
@ -90,8 +87,10 @@ int wrenSymbolTableEnsure(WrenVM* vm, SymbolTable* symbols,
|
||||
const char* name, size_t length);
|
||||
|
||||
// Looks up name in the symbol table. Returns its index if found or -1 if not.
|
||||
int wrenSymbolTableFind(const SymbolTable* symbols,
|
||||
const char* name, size_t length);
|
||||
int wrenSymbolTableFind(const SymbolTable* symbols,
|
||||
const char* name, size_t length);
|
||||
|
||||
void wrenBlackenSymbolTable(WrenVM* vm, SymbolTable* symbolTable);
|
||||
|
||||
// Returns the number of bytes needed to encode [value] in UTF-8.
|
||||
//
|
||||
|
||||
@ -159,27 +159,31 @@ ObjFiber* wrenNewFiber(WrenVM* vm, ObjClosure* closure)
|
||||
|
||||
ObjFiber* fiber = ALLOCATE(vm, ObjFiber);
|
||||
initObj(vm, &fiber->obj, OBJ_FIBER, vm->fiberClass);
|
||||
|
||||
fiber->stack = stack;
|
||||
fiber->stackTop = fiber->stack;
|
||||
fiber->stackCapacity = stackCapacity;
|
||||
|
||||
fiber->frames = frames;
|
||||
fiber->frameCapacity = INITIAL_CALL_FRAMES;
|
||||
fiber->stack = stack;
|
||||
fiber->stackCapacity = stackCapacity;
|
||||
wrenResetFiber(vm, fiber, closure);
|
||||
fiber->numFrames = 0;
|
||||
|
||||
return fiber;
|
||||
}
|
||||
|
||||
void wrenResetFiber(WrenVM* vm, ObjFiber* fiber, ObjClosure* closure)
|
||||
{
|
||||
// Reset everything.
|
||||
fiber->stackTop = fiber->stack;
|
||||
fiber->openUpvalues = NULL;
|
||||
fiber->caller = NULL;
|
||||
fiber->error = NULL_VAL;
|
||||
fiber->callerIsTrying = false;
|
||||
fiber->numFrames = 0;
|
||||
|
||||
if (closure != NULL)
|
||||
{
|
||||
// Initialize the first call frame.
|
||||
wrenAppendCallFrame(vm, fiber, closure, fiber->stack);
|
||||
|
||||
// Initialize the first call frame.
|
||||
if (closure != NULL) wrenAppendCallFrame(vm, fiber, closure, fiber->stack);
|
||||
// The first slot always holds the closure.
|
||||
fiber->stackTop[0] = OBJ_VAL(closure);
|
||||
fiber->stackTop++;
|
||||
}
|
||||
|
||||
return fiber;
|
||||
}
|
||||
|
||||
void wrenEnsureStack(WrenVM* vm, ObjFiber* fiber, int needed)
|
||||
@ -1137,11 +1141,12 @@ static void blackenModule(WrenVM* vm, ObjModule* module)
|
||||
wrenGrayValue(vm, module->variables.data[i]);
|
||||
}
|
||||
|
||||
wrenBlackenSymbolTable(vm, &module->variableNames);
|
||||
|
||||
wrenGrayObj(vm, (Obj*)module->name);
|
||||
|
||||
// Keep track of how much memory is still in use.
|
||||
vm->bytesAllocated += sizeof(ObjModule);
|
||||
// TODO: Track memory for symbol table and buffer.
|
||||
}
|
||||
|
||||
static void blackenRange(WrenVM* vm, ObjRange* range)
|
||||
@ -1296,9 +1301,8 @@ bool wrenValuesEqual(Value a, Value b)
|
||||
{
|
||||
ObjString* aString = (ObjString*)aObj;
|
||||
ObjString* bString = (ObjString*)bObj;
|
||||
return aString->length == bString->length &&
|
||||
aString->hash == bString->hash &&
|
||||
memcmp(aString->value, bString->value, aString->length) == 0;
|
||||
return aString->hash == bString->hash &&
|
||||
wrenStringEqualsCString(aString, bString->value, bString->length);
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define wren_value_h
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wren_common.h"
|
||||
#include "wren_utils.h"
|
||||
@ -102,7 +103,8 @@ typedef enum {
|
||||
typedef struct sObjClass ObjClass;
|
||||
|
||||
// Base struct for all heap-allocated objects.
|
||||
typedef struct sObj
|
||||
typedef struct sObj Obj;
|
||||
struct sObj
|
||||
{
|
||||
ObjType type;
|
||||
bool isDark;
|
||||
@ -112,7 +114,7 @@ typedef struct sObj
|
||||
|
||||
// The next object in the linked list of all currently allocated objects.
|
||||
struct sObj* next;
|
||||
} Obj;
|
||||
};
|
||||
|
||||
#if WREN_NAN_TAGGING
|
||||
|
||||
@ -145,7 +147,7 @@ typedef struct
|
||||
DECLARE_BUFFER(Value, Value);
|
||||
|
||||
// A heap-allocated string object.
|
||||
typedef struct
|
||||
struct sObjString
|
||||
{
|
||||
Obj obj;
|
||||
|
||||
@ -157,7 +159,7 @@ typedef struct
|
||||
|
||||
// Inline array of the string's bytes followed by a null terminator.
|
||||
char value[FLEXIBLE_ARRAY];
|
||||
} ObjString;
|
||||
};
|
||||
|
||||
// The dynamically allocated data structure for a variable that has been used
|
||||
// by a closure. Whenever a function accesses a variable declared in an
|
||||
@ -171,7 +173,7 @@ typedef struct
|
||||
// be closed. When that happens, the value gets copied off the stack into the
|
||||
// upvalue itself. That way, it can have a longer lifetime than the stack
|
||||
// variable.
|
||||
typedef struct sUpvalue
|
||||
typedef struct sObjUpvalue
|
||||
{
|
||||
// The object header. Note that upvalues have this because they are garbage
|
||||
// collected, but they are not first class Wren objects.
|
||||
@ -187,7 +189,7 @@ typedef struct sUpvalue
|
||||
|
||||
// Open upvalues are stored in a linked list by the fiber. This points to the
|
||||
// next upvalue in that list.
|
||||
struct sUpvalue* next;
|
||||
struct sObjUpvalue* next;
|
||||
} ObjUpvalue;
|
||||
|
||||
// The type of a primitive function.
|
||||
@ -628,10 +630,6 @@ ObjClosure* wrenNewClosure(WrenVM* vm, ObjFn* fn);
|
||||
// Creates a new fiber object that will invoke [closure].
|
||||
ObjFiber* wrenNewFiber(WrenVM* vm, ObjClosure* closure);
|
||||
|
||||
// Resets [fiber] back to an initial state where it is ready to invoke
|
||||
// [closure].
|
||||
void wrenResetFiber(WrenVM* vm, ObjFiber* fiber, ObjClosure* closure);
|
||||
|
||||
// Adds a new [CallFrame] to [fiber] invoking [closure] whose stack starts at
|
||||
// [stackStart].
|
||||
static inline void wrenAppendCallFrame(WrenVM* vm, ObjFiber* fiber,
|
||||
@ -733,7 +731,15 @@ Value wrenStringCodePointAt(WrenVM* vm, ObjString* string, uint32_t index);
|
||||
// Search for the first occurence of [needle] within [haystack] and returns its
|
||||
// zero-based offset. Returns `UINT32_MAX` if [haystack] does not contain
|
||||
// [needle].
|
||||
uint32_t wrenStringFind(ObjString* haystack, ObjString* needle, uint32_t startIndex);
|
||||
uint32_t wrenStringFind(ObjString* haystack, ObjString* needle,
|
||||
uint32_t startIndex);
|
||||
|
||||
// Returns true if [a] and [b] represent the same string.
|
||||
static inline bool wrenStringEqualsCString(const ObjString* a,
|
||||
const char* b, size_t length)
|
||||
{
|
||||
return a->length == length && memcmp(a->value, b, length) == 0;
|
||||
}
|
||||
|
||||
// Creates a new open upvalue pointing to [value] on the stack.
|
||||
ObjUpvalue* wrenNewUpvalue(WrenVM* vm, Value* value);
|
||||
|
||||
@ -151,6 +151,9 @@ void wrenCollectGarbage(WrenVM* vm)
|
||||
// Any object the compiler is using (if there is one).
|
||||
if (vm->compiler != NULL) wrenMarkCompiler(vm, vm->compiler);
|
||||
|
||||
// Method names.
|
||||
wrenBlackenSymbolTable(vm, &vm->methodNames);
|
||||
|
||||
// Now that we have grayed the roots, do a depth-first search over all of the
|
||||
// reachable objects.
|
||||
wrenBlackenObjects(vm);
|
||||
@ -421,7 +424,7 @@ static void runtimeError(WrenVM* vm)
|
||||
static void methodNotFound(WrenVM* vm, ObjClass* classObj, int symbol)
|
||||
{
|
||||
vm->fiber->error = wrenStringFormat(vm, "@ does not implement '$'.",
|
||||
OBJ_VAL(classObj->name), vm->methodNames.data[symbol].buffer);
|
||||
OBJ_VAL(classObj->name), vm->methodNames.data[symbol]->value);
|
||||
}
|
||||
|
||||
// Checks that [value], which must be a closure, does not require more
|
||||
@ -493,8 +496,8 @@ static ObjClosure* compileInModule(WrenVM* vm, Value name, const char* source,
|
||||
for (int i = 0; i < coreModule->variables.count; i++)
|
||||
{
|
||||
wrenDefineVariable(vm, module,
|
||||
coreModule->variableNames.data[i].buffer,
|
||||
coreModule->variableNames.data[i].length,
|
||||
coreModule->variableNames.data[i]->value,
|
||||
coreModule->variableNames.data[i]->length,
|
||||
coreModule->variables.data[i]);
|
||||
}
|
||||
}
|
||||
@ -730,7 +733,9 @@ static Value importModule(WrenVM* vm, Value name)
|
||||
// If the module is already loaded, we don't need to do anything.
|
||||
Value existing = wrenMapGet(vm->modules, name);
|
||||
if (!IS_UNDEFINED(existing)) return existing;
|
||||
|
||||
|
||||
wrenPushRoot(vm, AS_OBJ(name));
|
||||
|
||||
const char* source = NULL;
|
||||
bool allocatedSource = true;
|
||||
|
||||
@ -759,6 +764,7 @@ static Value importModule(WrenVM* vm, Value name)
|
||||
if (source == NULL)
|
||||
{
|
||||
vm->fiber->error = wrenStringFormat(vm, "Could not load module '@'.", name);
|
||||
wrenPopRoot(vm); // name.
|
||||
return NULL_VAL;
|
||||
}
|
||||
|
||||
@ -773,9 +779,12 @@ static Value importModule(WrenVM* vm, Value name)
|
||||
{
|
||||
vm->fiber->error = wrenStringFormat(vm,
|
||||
"Could not compile module '@'.", name);
|
||||
wrenPopRoot(vm); // name.
|
||||
return NULL_VAL;
|
||||
}
|
||||
|
||||
|
||||
wrenPopRoot(vm); // name.
|
||||
|
||||
// Return the closure that executes the module.
|
||||
return OBJ_VAL(moduleClosure);
|
||||
}
|
||||
@ -1306,20 +1315,18 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
|
||||
CASE_CODE(IMPORT_MODULE):
|
||||
{
|
||||
Value name = fn->constants.data[READ_SHORT()];
|
||||
|
||||
Value result = importModule(vm, name);
|
||||
// Make a slot on the stack for the module's fiber to place the return
|
||||
// value. It will be popped after this fiber is resumed. Store the
|
||||
// imported module's closure in the slot in case a GC happens when
|
||||
// invoking the closure.
|
||||
PUSH(importModule(vm, fn->constants.data[READ_SHORT()]));
|
||||
if (!IS_NULL(fiber->error)) RUNTIME_ERROR();
|
||||
|
||||
// Make a slot on the stack for the module's closure to place the return
|
||||
// value. It will be popped after the module body code returns.
|
||||
PUSH(NULL_VAL);
|
||||
|
||||
// If we get a closure, call it to execute the module body.
|
||||
if (IS_CLOSURE(result))
|
||||
if (IS_CLOSURE(PEEK()))
|
||||
{
|
||||
STORE_FRAME();
|
||||
ObjClosure* closure = AS_CLOSURE(result);
|
||||
ObjClosure* closure = AS_CLOSURE(PEEK());
|
||||
callFunction(vm, fiber, closure, 1);
|
||||
LOAD_FRAME();
|
||||
}
|
||||
@ -1327,7 +1334,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
{
|
||||
// The module has already been loaded. Remember it so we can import
|
||||
// variables from it if needed.
|
||||
vm->lastModule = AS_MODULE(result);
|
||||
vm->lastModule = AS_MODULE(PEEK());
|
||||
}
|
||||
|
||||
DISPATCH();
|
||||
@ -1477,7 +1484,7 @@ WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
|
||||
wrenPushRoot(vm, (Obj*)closure);
|
||||
ObjFiber* fiber = wrenNewFiber(vm, closure);
|
||||
wrenPopRoot(vm); // closure.
|
||||
|
||||
|
||||
return runInterpreter(vm, fiber);
|
||||
}
|
||||
|
||||
@ -1774,7 +1781,7 @@ void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
|
||||
int slot)
|
||||
{
|
||||
ASSERT(module != NULL, "Module cannot be NULL.");
|
||||
ASSERT(module != NULL, "Variable name cannot be NULL.");
|
||||
ASSERT(name != NULL, "Variable name cannot be NULL.");
|
||||
validateApiSlot(vm, slot);
|
||||
|
||||
Value moduleName = wrenStringFormat(vm, "$", module);
|
||||
|
||||
16
test/core/string/trim.wren
Normal file
16
test/core/string/trim.wren
Normal file
@ -0,0 +1,16 @@
|
||||
System.print("".trim() == "") // expect: true
|
||||
System.print("foo".trim() == "foo") // expect: true
|
||||
System.print(" \t\r\nfoo b\tar \t\r\n".trim() == "foo b\tar") // expect: true
|
||||
System.print(" \t\r\n \t\r\n".trim() == "") // expect: true
|
||||
System.print(" \n\n\tsøméஃthîng \n\n\t".trim() == "søméஃthîng") // expect: true
|
||||
|
||||
System.print("".trim("abc") == "") // expect: true
|
||||
System.print("foo".trim("abc") == "foo") // expect: true
|
||||
System.print("foo".trim("") == "foo") // expect: true
|
||||
System.print("cbacbfoobarab".trim("abc") == "foobar") // expect: true
|
||||
System.print("abcbacba".trim("abc") == "") // expect: true
|
||||
System.print("søméஃthîngsøméஃ".trim("ஃmésø") == "thîng") // expect: true
|
||||
|
||||
// 8-bit clean.
|
||||
System.print(" \t\ra\0b \t\r".trim() == "a\0b") // expect: true
|
||||
System.print("\0a\0b\0c\0".trim("c\0a") == "b") // expect: true
|
||||
1
test/core/string/trim_chars_not_string.wren
Normal file
1
test/core/string/trim_chars_not_string.wren
Normal file
@ -0,0 +1 @@
|
||||
"abracadabra".trim(123) // expect runtime error: Characters must be a string.
|
||||
16
test/core/string/trim_end.wren
Normal file
16
test/core/string/trim_end.wren
Normal file
@ -0,0 +1,16 @@
|
||||
System.print("".trimEnd() == "") // expect: true
|
||||
System.print("foo".trimEnd() == "foo") // expect: true
|
||||
System.print(" \t\r\nfoo b\tar \t\r\n".trimEnd() == " \t\r\nfoo b\tar") // expect: true
|
||||
System.print(" \t\r\n \t\r\n".trimEnd() == "") // expect: true
|
||||
System.print("søméஃthîng \n\n\t".trimEnd() == "søméஃthîng") // expect: true
|
||||
|
||||
System.print("".trimEnd("abc") == "") // expect: true
|
||||
System.print("foo".trimEnd("abc") == "foo") // expect: true
|
||||
System.print("foo".trimEnd("") == "foo") // expect: true
|
||||
System.print("cbacbfoobarab".trimEnd("abc") == "cbacbfoobar") // expect: true
|
||||
System.print("abcbacba".trimEnd("abc") == "") // expect: true
|
||||
System.print("søméஃthîngsøméஃ".trimEnd("ஃmésø") == "søméஃthîng") // expect: true
|
||||
|
||||
// 8-bit clean.
|
||||
System.print(" \t\ra\0b \t\r".trimEnd() == " \t\ra\0b") // expect: true
|
||||
System.print("\0a\0b\0c\0".trimEnd("c\0") == "\0a\0b") // expect: true
|
||||
1
test/core/string/trim_end_chars_not_string.wren
Normal file
1
test/core/string/trim_end_chars_not_string.wren
Normal file
@ -0,0 +1 @@
|
||||
"abracadabra".trimEnd(123) // expect runtime error: Characters must be a string.
|
||||
16
test/core/string/trim_start.wren
Normal file
16
test/core/string/trim_start.wren
Normal file
@ -0,0 +1,16 @@
|
||||
System.print("".trimStart() == "") // expect: true
|
||||
System.print("foo".trimStart() == "foo") // expect: true
|
||||
System.print(" \t\r\nfoo b\tar \t\r\n".trimStart() == "foo b\tar \t\r\n") // expect: true
|
||||
System.print(" \t\r\n \t\r\n".trimStart() == "") // expect: true
|
||||
System.print(" \n\n\tsøméஃthîng".trimStart() == "søméஃthîng") // expect: true
|
||||
|
||||
System.print("".trimStart("abc") == "") // expect: true
|
||||
System.print("foo".trimStart("abc") == "foo") // expect: true
|
||||
System.print("foo".trimStart("") == "foo") // expect: true
|
||||
System.print("cbacbfoobarab".trimStart("abc") == "foobarab") // expect: true
|
||||
System.print("abcbacba".trimStart("abc") == "") // expect: true
|
||||
System.print("søméஃthîng".trimStart("ஃmésø") == "thîng") // expect: true
|
||||
|
||||
// 8-bit clean.
|
||||
System.print(" \t\ra\0b".trimStart() == "a\0b") // expect: true
|
||||
System.print("\0a\0b\0c\0".trimStart("a\0") == "b\0c\0") // expect: true
|
||||
1
test/core/string/trim_start_chars_not_string.wren
Normal file
1
test/core/string/trim_start_chars_not_string.wren
Normal file
@ -0,0 +1 @@
|
||||
"abracadabra".trimStart(123) // expect runtime error: Characters must be a string.
|
||||
2
test/language/bom.wren
Normal file
2
test/language/bom.wren
Normal file
@ -0,0 +1,2 @@
|
||||
// This file should have a UTF-8 byte order mark
|
||||
System.print("ok") // expect: ok
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
var a0 = "value"
|
||||
var a1 = a0
|
||||
// Slot zero is always taken to hold the closure or receiver.
|
||||
var a1 = "value"
|
||||
var a2 = a1
|
||||
var a3 = a2
|
||||
var a4 = a3
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// Can have more than 256 local variables in a local scope, as long as they
|
||||
// Can have more than 255 local variables in a local scope, as long as they
|
||||
// aren't all in scope at the same time.
|
||||
|
||||
{
|
||||
{
|
||||
var a0 = "value a"
|
||||
var a1 = a0
|
||||
// Slot zero is always taken to hold the closure or receiver.
|
||||
var a1 = "value a"
|
||||
var a2 = a1
|
||||
var a3 = a2
|
||||
var a4 = a3
|
||||
@ -263,8 +263,8 @@
|
||||
}
|
||||
|
||||
{
|
||||
var b0 = "value b"
|
||||
var b1 = b0
|
||||
// Slot zero is always taken to hold the closure or receiver.
|
||||
var b1 = "value b"
|
||||
var b2 = b1
|
||||
var b3 = b2
|
||||
var b4 = b3
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
var a0 = "value"
|
||||
var a1 = a0
|
||||
// Slot zero is always taken to hold the closure or receiver.
|
||||
var a1 = "value"
|
||||
var a2 = a1
|
||||
var a3 = a2
|
||||
var a4 = a3
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
var a0 = "value"
|
||||
var a1 = a0
|
||||
// Slot zero is always taken to hold the closure or receiver.
|
||||
var a1 = "value"
|
||||
var a2 = a1
|
||||
var a3 = a2
|
||||
var a4 = a3
|
||||
|
||||
3
test/regression/494.wren
Normal file
3
test/regression/494.wren
Normal file
@ -0,0 +1,3 @@
|
||||
0[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
// expect error line 1
|
||||
// expect error line 4
|
||||
33
util/deploy_docs_from_travis.sh
Normal file
33
util/deploy_docs_from_travis.sh
Normal file
@ -0,0 +1,33 @@
|
||||
#!/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
|
||||
@ -8,5 +8,6 @@ Home-page: UNKNOWN
|
||||
Author: Robert Nystrom
|
||||
Author-email: UNKNOWN
|
||||
License: UNKNOWN
|
||||
Description-Content-Type: UNKNOWN
|
||||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
|
||||
@ -110,7 +110,7 @@ endif
|
||||
ifneq (,$(findstring darwin,$(OS)))
|
||||
SHARED_EXT := dylib
|
||||
else
|
||||
SHARED_LIB_FLAGS := -Wl,-soname,$@.so
|
||||
SHARED_LIB_FLAGS := -Wl,-soname,libwren.so
|
||||
SHARED_EXT := so
|
||||
|
||||
# Link in the right libraries needed by libuv on Windows and Linux.
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
Loading…
Reference in New Issue
Block a user