Merge branch 'cpp'

Conflicts:
	src/wren_vm.c
This commit is contained in:
Bob Nystrom 2015-01-15 21:17:08 -08:00
commit 8651d9c12a
14 changed files with 215 additions and 144 deletions

2
.gitignore vendored
View File

@ -6,8 +6,10 @@ build_xcode/
# Built files at the top level.
wren
wrend
wren-cpp
libwren.a
libwrend.a
libwren-cpp.a
# XCode user-specific stuff.
xcuserdata/

View File

@ -2,6 +2,7 @@ AR = ar rcu
# Compiler flags.
CFLAGS = -std=c99 -Wall -Werror
# TODO: Add -Wextra.
CPPFLAGS = -std=c++98 -Wall -Werror
DEBUG_CFLAGS = -O0 -DDEBUG -g
RELEASE_CFLAGS = -Os
@ -11,9 +12,13 @@ HEADERS = $(wildcard src/*.h)
OBJECTS = $(SOURCES:.c=.o)
# Don't include main.c in the shared library.
DEBUG_OBJECTS = $(subst build/debug/main.o,,$(addprefix build/debug/, $(notdir $(OBJECTS))))
RELEASE_OBJECTS = $(subst build/release/main.o,,$(addprefix build/release/, $(notdir $(OBJECTS))))
DEBUG_OBJECTS = $(addprefix build/debug/, $(notdir $(OBJECTS)))
RELEASE_OBJECTS = $(addprefix build/release/, $(notdir $(OBJECTS)))
RELEASE_CPP_OBJECTS = $(addprefix build/release-cpp/, $(notdir $(OBJECTS)))
DEBUG_LIB_OBJECTS = $(subst build/debug/main.o,,$(DEBUG_OBJECTS))
RELEASE_LIB_OBJECTS = $(subst build/release/main.o,,$(RELEASE_OBJECTS))
RELEASE_CPP_LIB_OBJECTS = $(subst build/release-cpp/main.o,,$(RELEASE_CPP_OBJECTS))
.PHONY: all clean test builtin docs watchdocs
@ -23,38 +28,53 @@ clean:
@rm -rf build wren wrend libwren.a libwrend.a
prep:
@mkdir -p build/debug build/release
@mkdir -p build/debug build/release build/release-cpp
# Debug build.
debug: prep wrend
debug: prep wrend libwrend.a
# Debug shared lib
libwrend.a: $(DEBUG_OBJECTS)
# Debug shared library.
libwrend.a: $(DEBUG_LIB_OBJECTS)
$(AR) $@ $^
# Debug command-line interpreter.
wrend: build/debug/main.o libwrend.a
$(CC) $(CFLAGS) $(DEBUG_CFLAGS) -Iinclude -o wrend $^ -lm
wrend: $(DEBUG_OBJECTS)
$(CC) $(CFLAGS) $(DEBUG_CFLAGS) -Iinclude -o $@ $^ -lm
# Debug object files.
build/debug/%.o: src/%.c include/wren.h $(HEADERS)
$(CC) -c -fPIC $(CFLAGS) $(DEBUG_CFLAGS) -Iinclude -o $@ $<
# Release build.
release: prep wren
release: prep wren libwren.a
# Release shared lib
libwren.a: $(RELEASE_OBJECTS)
# Release shared library.
libwren.a: $(RELEASE_LIB_OBJECTS)
$(AR) $@ $^
# Release command-line interpreter.
wren: build/release/main.o libwren.a
$(CC) $(CFLAGS) $(RELEASE_CFLAGS) -Iinclude -o wren $^ -lm
wren: $(RELEASE_OBJECTS)
$(CC) $(CFLAGS) $(RELEASE_CFLAGS) -Iinclude -o $@ $^ -lm
# Release object files.
build/release/%.o: src/%.c include/wren.h $(HEADERS)
$(CC) -c -fPIC $(CFLAGS) $(RELEASE_CFLAGS) -Iinclude -o $@ $<
# Release C++ build.
release-cpp: prep wren-cpp libwren-cpp.a
# Release C++ shared lib
libwren-cpp.a: $(RELEASE_CPP_LIB_OBJECTS)
$(AR) $@ $^
# Release C++ command-line interpreter.
wren-cpp: $(RELEASE_CPP_OBJECTS)
$(CC) $(CPPFLAGS) $(RELEASE_CFLAGS) -Iinclude -o $@ $^ -lm
# Release C++ object files.
build/release-cpp/%.o: src/%.c include/wren.h $(HEADERS)
$(CC) -c -fPIC $(CPPFLAGS) $(RELEASE_CFLAGS) -Iinclude -o $@ -x c++ $<
# Run the tests against the debug build of Wren.
test: debug
@./script/test.py $(suite)

View File

@ -37,8 +37,8 @@ while (!adjectives.isDone) IO.print(adjectives.call)
* **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's written in warning-free
standard C99.
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, [give it a try][try]! Even better, you can
[contribute to Wren itself][contribute].

View File

@ -38,8 +38,8 @@ a familiar, modern [syntax][].
* **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's written in warning-free
standard C99.
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, [give it a try][try]! Even better, you can
[contribute to Wren itself][contribute].

View File

@ -42,7 +42,7 @@ else:
passed = 0
failed = 0
skipped = defaultdict(int)
num_skipped = 0;
num_skipped = 0
def walk(dir, callback):

View File

@ -34,7 +34,7 @@ static char* readFile(const char* path)
rewind(file);
// Allocate a buffer for it.
char* buffer = malloc(fileSize + 1);
char* buffer = (char*)malloc(fileSize + 1);
failIf(buffer == NULL, 74, "Could not read file \"%s\".\n", path);
// Read the entire file.

View File

@ -901,10 +901,10 @@ static bool consumeLine(Compiler* compiler, const char* errorMessage)
// Variables and scopes --------------------------------------------------------
// Emits one bytecode instruction or argument. Returns its index.
static int emit(Compiler* compiler, Code code)
// Emits one bytecode instruction or single-byte argument. Returns its index.
static int emit(Compiler* compiler, uint8_t byte)
{
wrenByteBufferWrite(compiler->parser->vm, &compiler->bytecode, code);
wrenByteBufferWrite(compiler->parser->vm, &compiler->bytecode, byte);
// Assume the instruction is associated with the most recently consumed token.
wrenIntBufferWrite(compiler->parser->vm, &compiler->debugSourceLines,
@ -913,9 +913,16 @@ static int emit(Compiler* compiler, Code code)
return compiler->bytecode.count - 1;
}
// Emits one 16-bit argument, which will be written big endian.
static void emitShort(Compiler* compiler, uint16_t arg)
{
emit(compiler, (arg >> 8) & 0xff);
emit(compiler, arg & 0xff);
}
// Emits one bytecode instruction followed by a 8-bit argument. Returns the
// index of the argument in the bytecode.
static int emitByte(Compiler* compiler, Code instruction, uint8_t arg)
static int emitByteArg(Compiler* compiler, Code instruction, uint8_t arg)
{
emit(compiler, instruction);
return emit(compiler, arg);
@ -923,11 +930,10 @@ static int emitByte(Compiler* compiler, Code instruction, uint8_t arg)
// Emits one bytecode instruction followed by a 16-bit argument, which will be
// written big endian.
static void emitShort(Compiler* compiler, Code instruction, uint16_t arg)
static void emitShortArg(Compiler* compiler, Code instruction, uint16_t arg)
{
emit(compiler, instruction);
emit(compiler, (arg >> 8) & 0xff);
emit(compiler, arg & 0xff);
emitShort(compiler, arg);
}
// Emits [instruction] followed by a placeholder for a jump offset. The
@ -1026,7 +1032,7 @@ static void defineVariable(Compiler* compiler, int symbol)
// It's a global variable, so store the value in the global slot and then
// discard the temporary for the initializer.
emitShort(compiler, CODE_STORE_GLOBAL, symbol);
emitShortArg(compiler, CODE_STORE_GLOBAL, symbol);
emit(compiler, CODE_POP);
}
@ -1201,7 +1207,7 @@ static void loadLocal(Compiler* compiler, int slot)
return;
}
emitByte(compiler, CODE_LOAD_LOCAL, slot);
emitByteArg(compiler, CODE_LOAD_LOCAL, slot);
}
// Copies the identifier from the previously consumed `TOKEN_NAME` into [name],
@ -1267,20 +1273,20 @@ static ObjFn* endCompiler(Compiler* compiler,
// We can just load and run the function directly.
if (compiler->numUpvalues == 0)
{
emitShort(compiler->parent, CODE_CONSTANT, constant);
emitShortArg(compiler->parent, CODE_CONSTANT, constant);
}
else
{
// Capture the upvalues in the new closure object.
emitShort(compiler->parent, CODE_CLOSURE, constant);
emitShortArg(compiler->parent, CODE_CLOSURE, constant);
// Emit arguments for each upvalue to know whether to capture a local or
// an upvalue.
// TODO: Do something more efficient here?
for (int i = 0; i < compiler->numUpvalues; i++)
{
emitByte(compiler->parent, compiler->upvalues[i].isLocal ? 1 : 0,
compiler->upvalues[i].index);
emit(compiler->parent, compiler->upvalues[i].isLocal ? 1 : 0);
emit(compiler->parent, compiler->upvalues[i].index);
}
}
}
@ -1316,13 +1322,6 @@ typedef enum
PREC_CALL // . () []
} Precedence;
// Forward declarations since the grammar is recursive.
static void expression(Compiler* compiler);
static void statement(Compiler* compiler);
static void definition(Compiler* compiler);
static void parsePrecedence(Compiler* compiler, bool allowAssignment,
Precedence precedence);
typedef void (*GrammarFn)(Compiler*, bool allowAssignment);
typedef void (*SignatureFn)(Compiler* compiler, char* name, int* length);
@ -1336,7 +1335,13 @@ typedef struct
const char* name;
} GrammarRule;
GrammarRule rules[];
// Forward declarations since the grammar is recursive.
static GrammarRule* getRule(TokenType type);
static void expression(Compiler* compiler);
static void statement(Compiler* compiler);
static void definition(Compiler* compiler);
static void parsePrecedence(Compiler* compiler, bool allowAssignment,
Precedence precedence);
// Replaces the placeholder argument for a previous CODE_JUMP or CODE_JUMP_IF
// instruction with an offset that jumps to the current end of bytecode.
@ -1507,8 +1512,8 @@ static void methodCall(Compiler* compiler, Code instruction,
// TODO: Allow Grace-style mixfix methods?
emitShort(compiler, instruction + numArgs,
methodSymbol(compiler, name, length));
emitShortArg(compiler, (Code)(instruction + numArgs),
methodSymbol(compiler, name, length));
}
// Compiles a call whose name is the previously consumed token. This includes
@ -1531,7 +1536,8 @@ static void namedCall(Compiler* compiler, bool allowAssignment,
// Compile the assigned value.
expression(compiler);
emitShort(compiler, instruction + 1, methodSymbol(compiler, name, length));
emitShortArg(compiler, (Code)(instruction + 1),
methodSymbol(compiler, name, length));
}
else
{
@ -1551,7 +1557,7 @@ static void loadThis(Compiler* compiler)
}
else
{
emitByte(compiler, loadInstruction, index);
emitByteArg(compiler, loadInstruction, index);
}
}
@ -1581,21 +1587,21 @@ static void list(Compiler* compiler, bool allowAssignment)
// Create the list.
// TODO: Handle lists >255 elements.
emitByte(compiler, CODE_LIST, numElements);
emitByteArg(compiler, CODE_LIST, numElements);
}
// Unary operators like `-foo`.
static void unaryOp(Compiler* compiler, bool allowAssignment)
{
GrammarRule* rule = &rules[compiler->parser->previous.type];
GrammarRule* rule = getRule(compiler->parser->previous.type);
ignoreNewlines(compiler);
// Compile the argument.
parsePrecedence(compiler, false, PREC_UNARY + 1);
parsePrecedence(compiler, false, (Precedence)(PREC_UNARY + 1));
// Call the operator method on the left-hand side.
emitShort(compiler, CODE_CALL_0, methodSymbol(compiler, rule->name, 1));
emitShortArg(compiler, CODE_CALL_0, methodSymbol(compiler, rule->name, 1));
}
static void boolean(Compiler* compiler, bool allowAssignment)
@ -1669,13 +1675,13 @@ static void field(Compiler* compiler, bool allowAssignment)
if (compiler->parent != NULL &&
compiler->parent->enclosingClass == enclosingClass)
{
emitByte(compiler, isLoad ? CODE_LOAD_FIELD_THIS : CODE_STORE_FIELD_THIS,
field);
emitByteArg(compiler, isLoad ? CODE_LOAD_FIELD_THIS : CODE_STORE_FIELD_THIS,
field);
}
else
{
loadThis(compiler);
emitByte(compiler, isLoad ? CODE_LOAD_FIELD : CODE_STORE_FIELD, field);
emitByteArg(compiler, isLoad ? CODE_LOAD_FIELD : CODE_STORE_FIELD, field);
}
}
@ -1696,13 +1702,13 @@ static void variable(Compiler* compiler, bool allowAssignment, int index,
switch (loadInstruction)
{
case CODE_LOAD_LOCAL:
emitByte(compiler, CODE_STORE_LOCAL, index);
emitByteArg(compiler, CODE_STORE_LOCAL, index);
break;
case CODE_LOAD_UPVALUE:
emitByte(compiler, CODE_STORE_UPVALUE, index);
emitByteArg(compiler, CODE_STORE_UPVALUE, index);
break;
case CODE_LOAD_GLOBAL:
emitShort(compiler, CODE_STORE_GLOBAL, index);
emitShortArg(compiler, CODE_STORE_GLOBAL, index);
break;
default:
UNREACHABLE();
@ -1710,7 +1716,7 @@ static void variable(Compiler* compiler, bool allowAssignment, int index,
}
else if (loadInstruction == CODE_LOAD_GLOBAL)
{
emitShort(compiler, loadInstruction, index);
emitShortArg(compiler, loadInstruction, index);
}
else if (loadInstruction == CODE_LOAD_LOCAL)
{
@ -1718,7 +1724,7 @@ static void variable(Compiler* compiler, bool allowAssignment, int index,
}
else
{
emitByte(compiler, loadInstruction, index);
emitByteArg(compiler, loadInstruction, index);
}
}
@ -1844,7 +1850,7 @@ static void number(Compiler* compiler, bool allowAssignment)
int constant = addConstant(compiler, NUM_VAL(value));
// Compile the code to load the constant.
emitShort(compiler, CODE_CONSTANT, constant);
emitShortArg(compiler, CODE_CONSTANT, constant);
}
static void string(Compiler* compiler, bool allowAssignment)
@ -1856,7 +1862,7 @@ static void string(Compiler* compiler, bool allowAssignment)
wrenByteBufferClear(compiler->parser->vm, &compiler->parser->string);
// Compile the code to load the constant.
emitShort(compiler, CODE_CONSTANT, constant);
emitShortArg(compiler, CODE_CONSTANT, constant);
}
static void super_(Compiler* compiler, bool allowAssignment)
@ -1957,8 +1963,8 @@ static void subscript(Compiler* compiler, bool allowAssignment)
}
// Compile the method call.
emitShort(compiler, CODE_CALL_0 + numArgs,
methodSymbol(compiler, name, length));
emitShortArg(compiler, (Code)(CODE_CALL_0 + numArgs),
methodSymbol(compiler, name, length));
}
static void call(Compiler* compiler, bool allowAssignment)
@ -1979,7 +1985,8 @@ static void new_(Compiler* compiler, bool allowAssignment)
}
// The leading space in the name is to ensure users can't call it directly.
emitShort(compiler, CODE_CALL_0, methodSymbol(compiler, " instantiate", 12));
emitShortArg(compiler, CODE_CALL_0,
methodSymbol(compiler, " instantiate", 12));
// Invoke the constructor on the new instance.
char name[MAX_METHOD_SIGNATURE];
@ -1997,7 +2004,7 @@ static void is(Compiler* compiler, bool allowAssignment)
emit(compiler, CODE_IS);
}
static void and(Compiler* compiler, bool allowAssignment)
static void and_(Compiler* compiler, bool allowAssignment)
{
ignoreNewlines(compiler);
@ -2007,7 +2014,7 @@ static void and(Compiler* compiler, bool allowAssignment)
patchJump(compiler, jump);
}
static void or(Compiler* compiler, bool allowAssignment)
static void or_(Compiler* compiler, bool allowAssignment)
{
ignoreNewlines(compiler);
@ -2046,16 +2053,16 @@ static void conditional(Compiler* compiler, bool allowAssignment)
void infixOp(Compiler* compiler, bool allowAssignment)
{
GrammarRule* rule = &rules[compiler->parser->previous.type];
GrammarRule* rule = getRule(compiler->parser->previous.type);
// An infix operator cannot end an expression.
ignoreNewlines(compiler);
// Compile the right-hand side.
parsePrecedence(compiler, false, rule->precedence + 1);
parsePrecedence(compiler, false, (Precedence)(rule->precedence + 1));
// Call the operator method on the left-hand side.
emitShort(compiler, CODE_CALL_1, methodSymbol(compiler, rule->name, 0));
emitShortArg(compiler, CODE_CALL_1, methodSymbol(compiler, rule->name, 0));
}
// Compiles a method signature for an infix operator.
@ -2150,9 +2157,9 @@ GrammarRule rules[] =
/* TOKEN_PLUS */ INFIX_OPERATOR(PREC_TERM, "+ "),
/* TOKEN_MINUS */ OPERATOR("- "),
/* TOKEN_PIPE */ INFIX_OPERATOR(PREC_BITWISE, "| "),
/* TOKEN_PIPEPIPE */ INFIX(PREC_LOGIC, or),
/* TOKEN_PIPEPIPE */ INFIX(PREC_LOGIC, or_),
/* TOKEN_AMP */ INFIX_OPERATOR(PREC_BITWISE, "& "),
/* TOKEN_AMPAMP */ INFIX(PREC_LOGIC, and),
/* TOKEN_AMPAMP */ INFIX(PREC_LOGIC, and_),
/* TOKEN_BANG */ PREFIX_OPERATOR("!"),
/* TOKEN_TILDE */ PREFIX_OPERATOR("~"),
/* TOKEN_QUESTION */ INFIX(PREC_ASSIGNMENT, conditional),
@ -2190,6 +2197,12 @@ GrammarRule rules[] =
/* TOKEN_EOF */ UNUSED
};
// Gets the [GrammarRule] associated with tokens of [type].
static GrammarRule* getRule(TokenType type)
{
return &rules[type];
}
// The main entrypoint for the top-down operator precedence parser.
void parsePrecedence(Compiler* compiler, bool allowAssignment,
Precedence precedence)
@ -2247,7 +2260,7 @@ void block(Compiler* compiler)
static int getNumArguments(const uint8_t* bytecode, const Value* constants,
int ip)
{
Code instruction = bytecode[ip];
Code instruction = (Code)bytecode[ip];
switch (instruction)
{
case CODE_NULL:
@ -2374,7 +2387,7 @@ static void endLoop(Compiler* compiler)
{
int loopOffset = compiler->bytecode.count - compiler->loop->start + 2;
// TODO: Check for overflow.
emitShort(compiler, CODE_LOOP, loopOffset);
emitShortArg(compiler, CODE_LOOP, loopOffset);
patchJump(compiler, compiler->loop->exitJump);
@ -2462,10 +2475,10 @@ static void forStatement(Compiler* compiler)
loadLocal(compiler, seqSlot);
loadLocal(compiler, iterSlot);
emitShort(compiler, CODE_CALL_1, methodSymbol(compiler, "iterate ", 8));
emitShortArg(compiler, CODE_CALL_1, methodSymbol(compiler, "iterate ", 8));
// Store the iterator back in its local for the next iteration.
emitByte(compiler, CODE_STORE_LOCAL, iterSlot);
emitByteArg(compiler, CODE_STORE_LOCAL, iterSlot);
// TODO: We can probably get this working with a bit less stack juggling.
testExitLoop(compiler);
@ -2474,8 +2487,8 @@ static void forStatement(Compiler* compiler)
loadLocal(compiler, seqSlot);
loadLocal(compiler, iterSlot);
emitShort(compiler, CODE_CALL_1,
methodSymbol(compiler, "iteratorValue ", 14));
emitShortArg(compiler, CODE_CALL_1,
methodSymbol(compiler, "iteratorValue ", 14));
// Bind the loop variable in its own scope. This ensures we get a fresh
// variable each iteration so that closures for it don't all see the same one.
@ -2630,7 +2643,7 @@ static void classDefinition(Compiler* compiler)
int nameConstant = addConstant(compiler, wrenNewString(compiler->parser->vm,
compiler->parser->previous.start, compiler->parser->previous.length));
emitShort(compiler, CODE_CONSTANT, nameConstant);
emitShortArg(compiler, CODE_CONSTANT, nameConstant);
// Load the superclass (if there is one).
if (match(compiler, TOKEN_IS))
@ -2646,7 +2659,7 @@ static void classDefinition(Compiler* compiler)
// Store a placeholder for the number of fields argument. We don't know
// the value until we've compiled all the methods to see which fields are
// used.
int numFieldsInstruction = emitByte(compiler, CODE_CLASS, 255);
int numFieldsInstruction = emitByteArg(compiler, CODE_CLASS, 255);
// Store it in its name.
defineVariable(compiler, symbol);
@ -2706,7 +2719,7 @@ static void classDefinition(Compiler* compiler)
// Load the class.
if (isGlobal)
{
emitShort(compiler, CODE_LOAD_GLOBAL, symbol);
emitShortArg(compiler, CODE_LOAD_GLOBAL, symbol);
}
else
{
@ -2714,7 +2727,7 @@ static void classDefinition(Compiler* compiler)
}
// Define the method.
emitShort(compiler, instruction, methodSymbol);
emitShortArg(compiler, instruction, methodSymbol);
// Don't require a newline after the last definition.
if (match(compiler, TOKEN_RIGHT_BRACE)) break;
@ -2835,7 +2848,7 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
int ip = 0;
for (;;)
{
Code instruction = fn->bytecode[ip++];
Code instruction = (Code)fn->bytecode[ip++];
switch (instruction)
{
case CODE_LOAD_FIELD:

View File

@ -36,7 +36,7 @@ static int debugPrintInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
{
int start = i;
uint8_t* bytecode = fn->bytecode;
Code code = bytecode[i];
Code code = (Code)bytecode[i];
int line = fn->debug->sourceLines[i];
if (lastLine == NULL || *lastLine != line)

View File

@ -25,7 +25,8 @@ void wrenSymbolTableClear(WrenVM* vm, SymbolTable* symbols)
int wrenSymbolTableAdd(WrenVM* vm, SymbolTable* symbols, const char* name,
size_t length)
{
char* heapString = wrenReallocate(vm, NULL, 0, sizeof(char) * (length + 1));
char* heapString = (char*)wrenReallocate(vm, NULL, 0,
sizeof(char) * (length + 1));
strncpy(heapString, name, length);
heapString[length] = '\0';

View File

@ -34,12 +34,13 @@
wrenReallocate(vm, buffer->data, 0, 0); \
wren##name##BufferInit(vm, buffer); \
} \
\
void wren##name##BufferWrite(WrenVM* vm, name##Buffer* buffer, type data) \
{ \
if (buffer->capacity < buffer->count + 1) \
{ \
int capacity = buffer->capacity == 0 ? 8 : buffer->capacity * 2; \
buffer->data = wrenReallocate(vm, buffer->data, \
buffer->data = (type*)wrenReallocate(vm, buffer->data, \
buffer->capacity * sizeof(type), capacity * sizeof(type)); \
buffer->capacity = capacity; \
} \

View File

@ -18,10 +18,12 @@
DEFINE_BUFFER(Value, Value);
DEFINE_BUFFER(Method, Method);
static void* allocate(WrenVM* vm, size_t size)
{
return wrenReallocate(vm, NULL, 0, size);
}
#define ALLOCATE(vm, type) \
((type*)wrenReallocate(vm, NULL, 0, sizeof(type)))
#define ALLOCATE_FLEX(vm, type, extra) \
((type*)wrenReallocate(vm, NULL, 0, sizeof(type) + extra))
#define ALLOCATE_ARRAY(vm, type, count) \
((type*)wrenReallocate(vm, NULL, 0, sizeof(type) * count))
static void initObj(WrenVM* vm, Obj* obj, ObjType type, ObjClass* classObj)
{
@ -34,7 +36,7 @@ static void initObj(WrenVM* vm, Obj* obj, ObjType type, ObjClass* classObj)
ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name)
{
ObjClass* classObj = allocate(vm, sizeof(ObjClass));
ObjClass* classObj = ALLOCATE(vm, ObjClass);
initObj(vm, &classObj->obj, OBJ_CLASS, NULL);
classObj->superclass = NULL;
classObj->numFields = numFields;
@ -116,8 +118,8 @@ void wrenBindMethod(WrenVM* vm, ObjClass* classObj, int symbol, Method method)
ObjClosure* wrenNewClosure(WrenVM* vm, ObjFn* fn)
{
ObjClosure* closure = allocate(vm,
sizeof(ObjClosure) + sizeof(Upvalue*) * fn->numUpvalues);
ObjClosure* closure = ALLOCATE_FLEX(vm, ObjClosure,
sizeof(Upvalue*) * fn->numUpvalues);
initObj(vm, &closure->obj, OBJ_CLOSURE, vm->fnClass);
closure->fn = fn;
@ -131,7 +133,7 @@ ObjClosure* wrenNewClosure(WrenVM* vm, ObjFn* fn)
ObjFiber* wrenNewFiber(WrenVM* vm, Obj* fn)
{
ObjFiber* fiber = allocate(vm, sizeof(ObjFiber));
ObjFiber* fiber = ALLOCATE(vm, ObjFiber);
initObj(vm, &fiber->obj, OBJ_FIBER, vm->fiberClass);
// Push the stack frame for the function.
@ -169,25 +171,25 @@ ObjFn* wrenNewFunction(WrenVM* vm, Value* constants, int numConstants,
Value* copiedConstants = NULL;
if (numConstants > 0)
{
copiedConstants = allocate(vm, sizeof(Value) * numConstants);
copiedConstants = ALLOCATE_ARRAY(vm, Value, numConstants);
for (int i = 0; i < numConstants; i++)
{
copiedConstants[i] = constants[i];
}
}
FnDebug* debug = allocate(vm, sizeof(FnDebug));
FnDebug* debug = ALLOCATE(vm, FnDebug);
debug->sourcePath = debugSourcePath;
// Copy the function's name.
debug->name = allocate(vm, debugNameLength + 1);
debug->name = ALLOCATE_ARRAY(vm, char, debugNameLength + 1);
strncpy(debug->name, debugName, debugNameLength);
debug->name[debugNameLength] = '\0';
debug->sourceLines = sourceLines;
ObjFn* fn = allocate(vm, sizeof(ObjFn));
ObjFn* fn = ALLOCATE(vm, ObjFn);
initObj(vm, &fn->obj, OBJ_FN, vm->fnClass);
// TODO: Should eventually copy this instead of taking ownership. When the
@ -207,8 +209,8 @@ ObjFn* wrenNewFunction(WrenVM* vm, Value* constants, int numConstants,
Value wrenNewInstance(WrenVM* vm, ObjClass* classObj)
{
ObjInstance* instance = allocate(vm,
sizeof(ObjInstance) + classObj->numFields * sizeof(Value));
ObjInstance* instance = ALLOCATE_FLEX(vm, ObjInstance,
classObj->numFields * sizeof(Value));
initObj(vm, &instance->obj, OBJ_INSTANCE, classObj);
// Initialize fields to null.
@ -227,10 +229,10 @@ ObjList* wrenNewList(WrenVM* vm, int numElements)
Value* elements = NULL;
if (numElements > 0)
{
elements = allocate(vm, sizeof(Value) * numElements);
elements = ALLOCATE_ARRAY(vm, Value, numElements);
}
ObjList* list = allocate(vm, sizeof(ObjList));
ObjList* list = ALLOCATE(vm, ObjList);
initObj(vm, &list->obj, OBJ_LIST, vm->listClass);
list->capacity = numElements;
list->count = numElements;
@ -247,7 +249,7 @@ static void ensureListCapacity(WrenVM* vm, ObjList* list, int count)
if (capacity < LIST_MIN_CAPACITY) capacity = LIST_MIN_CAPACITY;
list->capacity *= 2;
list->elements = wrenReallocate(vm, list->elements,
list->elements = (Value*)wrenReallocate(vm, list->elements,
list->capacity * sizeof(Value), capacity * sizeof(Value));
// TODO: Handle allocation failure.
list->capacity = capacity;
@ -297,7 +299,7 @@ Value wrenListRemoveAt(WrenVM* vm, ObjList* list, int index)
// If we have too much excess capacity, shrink it.
if (list->capacity / LIST_GROW_FACTOR >= list->count)
{
list->elements = wrenReallocate(vm, list->elements,
list->elements = (Value*)wrenReallocate(vm, list->elements,
sizeof(Value) * list->capacity,
sizeof(Value) * (list->capacity / LIST_GROW_FACTOR));
list->capacity /= LIST_GROW_FACTOR;
@ -311,7 +313,7 @@ Value wrenListRemoveAt(WrenVM* vm, ObjList* list, int index)
Value wrenNewRange(WrenVM* vm, double from, double to, bool isInclusive)
{
ObjRange* range = allocate(vm, sizeof(ObjRange) + 16);
ObjRange* range = ALLOCATE(vm, ObjRange);
initObj(vm, &range->obj, OBJ_RANGE, vm->rangeClass);
range->from = from;
range->to = to;
@ -339,7 +341,7 @@ Value wrenNewString(WrenVM* vm, const char* text, size_t length)
Value wrenNewUninitializedString(WrenVM* vm, size_t length)
{
ObjString* string = allocate(vm, sizeof(ObjString) + length + 1);
ObjString* string = ALLOCATE_FLEX(vm, ObjString, length + 1);
initObj(vm, &string->obj, OBJ_STRING, vm->stringClass);
string->length = (int)length;
@ -362,7 +364,7 @@ ObjString* wrenStringConcat(WrenVM* vm, const char* left, const char* right)
Upvalue* wrenNewUpvalue(WrenVM* vm, Value* value)
{
Upvalue* upvalue = allocate(vm, sizeof(Upvalue));
Upvalue* upvalue = ALLOCATE(vm, Upvalue);
// Upvalues are never used as first-class objects, so don't need a class.
initObj(vm, &upvalue->obj, OBJ_UPVALUE, NULL);
@ -626,19 +628,6 @@ ObjClass* wrenGetClass(WrenVM* vm, Value value)
return wrenGetClassInline(vm, value);
}
bool wrenValuesEqual(Value a, Value b)
{
#if WREN_NAN_TAGGING
// Value types have unique bit representations and we compare object types
// by identity (i.e. pointer), so all we need to do is compare the bits.
return a.bits == b.bits;
#else
if (a.type != b.type) return false;
if (a.type == VAL_NUM) return a.num == b.num;
return a.obj == b.obj;
#endif
}
static void printList(ObjList* list)
{
printf("[");

View File

@ -82,11 +82,7 @@ typedef struct sObj
#if WREN_NAN_TAGGING
typedef union
{
double num;
uint64_t bits;
} Value;
typedef uint64_t Value;
#else
@ -378,7 +374,7 @@ typedef struct
#define AS_LIST(value) ((ObjList*)AS_OBJ(value))
// Value -> double.
#define AS_NUM(v) ((v).num)
#define AS_NUM(value) (wrenValueToNum(value))
// Value -> ObjRange*.
#define AS_RANGE(v) ((ObjRange*)AS_OBJ(v))
@ -392,6 +388,9 @@ typedef struct
// Convert [boolean] to a boolean [Value].
#define BOOL_VAL(boolean) (boolean ? TRUE_VAL : FALSE_VAL)
// double -> Value.
#define NUM_VAL(num) (wrenNumToValue(num))
// Convert [obj], an `Obj*`, to a [Value].
#define OBJ_VAL(obj) (wrenObjectToValue((Obj*)(obj)))
@ -482,18 +481,18 @@ typedef struct
#define QNAN ((uint64_t)0x7ffc000000000000)
// If the NaN bits are set, it's not a number.
#define IS_NUM(value) (((value).bits & QNAN) != QNAN)
#define IS_NUM(value) (((value) & QNAN) != QNAN)
// Singleton values are NaN with the sign bit cleared. (This includes the
// normal value of the actual NaN value used in numeric arithmetic.)
#define IS_SINGLETON(value) (((value).bits & (QNAN | SIGN_BIT)) == QNAN)
#define IS_SINGLETON(value) (((value) & (QNAN | SIGN_BIT)) == QNAN)
// An object pointer is a NaN with a set sign bit.
#define IS_OBJ(value) (((value).bits & (QNAN | SIGN_BIT)) == (QNAN | SIGN_BIT))
#define IS_OBJ(value) (((value) & (QNAN | SIGN_BIT)) == (QNAN | SIGN_BIT))
#define IS_FALSE(value) ((value).bits == FALSE_VAL.bits)
#define IS_NULL(value) ((value).bits == (QNAN | TAG_NULL))
#define IS_UNDEFINED(value) ((value).bits == (QNAN | TAG_UNDEFINED))
#define IS_FALSE(value) ((value) == FALSE_VAL)
#define IS_NULL(value) ((value) == (QNAN | TAG_NULL))
#define IS_UNDEFINED(value) ((value) == (QNAN | TAG_UNDEFINED))
// Masks out the tag bits used to identify the singleton value.
#define MASK_TAG (7)
@ -508,14 +507,11 @@ typedef struct
#define TAG_UNUSED3 (6)
#define TAG_UNUSED4 (7)
// double -> Value.
#define NUM_VAL(n) ((Value)(double)(n))
// Value -> 0 or 1.
#define AS_BOOL(value) ((value).bits == TRUE_VAL.bits)
#define AS_BOOL(value) ((value) == TRUE_VAL)
// Value -> Obj*.
#define AS_OBJ(value) ((Obj*)((value).bits & ~(SIGN_BIT | QNAN)))
#define AS_OBJ(value) ((Obj*)((value) & ~(SIGN_BIT | QNAN)))
// Singleton values.
#define NULL_VAL ((Value)(uint64_t)(QNAN | TAG_NULL))
@ -524,7 +520,7 @@ typedef struct
#define UNDEFINED_VAL ((Value)(uint64_t)(QNAN | TAG_UNDEFINED))
// Gets the singleton type tag for a Value (which must be a singleton).
#define GET_TAG(value) ((int)((value).bits & MASK_TAG))
#define GET_TAG(value) ((int)((value) & MASK_TAG))
#else
@ -542,9 +538,6 @@ typedef struct
#define IS_NUM(value) ((value).type == VAL_NUM)
#define IS_UNDEFINED(value) ((value).type == VAL_UNDEFINED)
// double -> Value.
#define NUM_VAL(n) ((Value){ VAL_NUM, n, NULL })
// Singleton values.
#define FALSE_VAL ((Value){ VAL_FALSE, 0.0, NULL })
#define NULL_VAL ((Value){ VAL_NULL, 0.0, NULL })
@ -645,7 +638,18 @@ ObjClass* wrenGetClass(WrenVM* vm, Value value);
// Returns true if [a] and [b] are strictly equal using built-in equality
// semantics. This is identity for object values, and value equality for others.
bool wrenValuesEqual(Value a, Value b);
static inline bool wrenValuesEqual(Value a, Value b)
{
#if WREN_NAN_TAGGING
// Value types have unique bit representations and we compare object types
// by identity (i.e. pointer), so all we need to do is compare the bits.
return a == b;
#else
if (a.type != b.type) return false;
if (a.type == VAL_NUM) return a.num == b.num;
return a.obj == b.obj;
#endif
}
// TODO: Need to decide if this is for user output of values, or for debug
// tracing.
@ -656,7 +660,7 @@ void wrenPrintValue(Value value);
static inline bool wrenIsBool(Value value)
{
#if WREN_NAN_TAGGING
return value.bits == TRUE_VAL.bits || value.bits == FALSE_VAL.bits;
return value == TRUE_VAL || value == FALSE_VAL;
#else
return value.type == VAL_FALSE || value.type == VAL_TRUE;
#endif
@ -682,4 +686,42 @@ static inline Value wrenObjectToValue(Obj* obj)
#endif
}
// Interprets [value] as a [double].
static inline double wrenValueToNum(Value value)
{
#if WREN_NAN_TAGGING
// Use a union to let us reinterpret the uint64_t bits back to the double
// value it actually stores.
union
{
uint64_t bits;
double num;
} data;
data.bits = value;
return data.num;
#else
return value.num;
#endif
}
// Converts [num] to a [Value].
static inline Value wrenNumToValue(double num)
{
#if WREN_NAN_TAGGING
// Use a union to let us reinterpret the bits making up the double as an
// opaque blob of bits.
union
{
uint64_t bits;
double num;
} data;
data.num = num;
return data.bits;
#else
return (Value){ VAL_NUM, n, NULL };
#endif
}
#endif

View File

@ -32,7 +32,7 @@ WrenVM* wrenNewVM(WrenConfiguration* configuration)
reallocate = configuration->reallocateFn;
}
WrenVM* vm = reallocate(NULL, 0, sizeof(WrenVM));
WrenVM* vm = (WrenVM*)reallocate(NULL, 0, sizeof(WrenVM));
vm->reallocate = reallocate;
@ -520,15 +520,15 @@ static bool runInterpreter(WrenVM* vm)
}
#else
#define DISPATCH() goto *dispatchTable[instruction = READ_BYTE()];
#define DISPATCH() goto *dispatchTable[instruction = (Code)READ_BYTE()];
#endif
#else
#define INTERPRET_LOOP interpretLoop: switch (instruction = READ_BYTE())
#define CASE_CODE(name) case CODE_##name
#define DISPATCH() goto interpretLoop
#define INTERPRET_LOOP loop: switch (instruction = (Code)READ_BYTE())
#define CASE_CODE(name) case CODE_##name
#define DISPATCH() goto loop
#endif

View File

@ -51,6 +51,9 @@ typedef enum
CODE_LOAD_LOCAL_7,
CODE_LOAD_LOCAL_8,
// Note: The compiler assumes the following _STORE instructions always
// immediately follow their corresponding _LOAD ones.
// Pushes the value in local slot [arg].
CODE_LOAD_LOCAL,