diff --git a/.gitignore b/.gitignore index 4cb62bea..17a8b778 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/Makefile b/Makefile index a2bbc309..f3a28397 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/README.md b/README.md index bc2e2384..563efbdd 100644 --- a/README.md +++ b/README.md @@ -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]. diff --git a/doc/site/index.markdown b/doc/site/index.markdown index e328c4f2..2e717be1 100644 --- a/doc/site/index.markdown +++ b/doc/site/index.markdown @@ -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]. diff --git a/script/test.py b/script/test.py index 80874679..557f78c2 100755 --- a/script/test.py +++ b/script/test.py @@ -42,7 +42,7 @@ else: passed = 0 failed = 0 skipped = defaultdict(int) -num_skipped = 0; +num_skipped = 0 def walk(dir, callback): diff --git a/src/main.c b/src/main.c index f2b5534f..11d9c541 100644 --- a/src/main.c +++ b/src/main.c @@ -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. diff --git a/src/wren_compiler.c b/src/wren_compiler.c index 5f74c38b..2b615da6 100644 --- a/src/wren_compiler.c +++ b/src/wren_compiler.c @@ -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: diff --git a/src/wren_debug.c b/src/wren_debug.c index 3fd6a1c3..ce32f1ae 100644 --- a/src/wren_debug.c +++ b/src/wren_debug.c @@ -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) diff --git a/src/wren_utils.c b/src/wren_utils.c index 8486e85d..87e033aa 100644 --- a/src/wren_utils.c +++ b/src/wren_utils.c @@ -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'; diff --git a/src/wren_utils.h b/src/wren_utils.h index 94e693ad..ba6c668c 100644 --- a/src/wren_utils.h +++ b/src/wren_utils.h @@ -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; \ } \ diff --git a/src/wren_value.c b/src/wren_value.c index 638a6662..6ee6c12f 100644 --- a/src/wren_value.c +++ b/src/wren_value.c @@ -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("["); diff --git a/src/wren_value.h b/src/wren_value.h index bf4c9021..e0464779 100644 --- a/src/wren_value.h +++ b/src/wren_value.h @@ -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 diff --git a/src/wren_vm.c b/src/wren_vm.c index b7a24723..bd0abf73 100644 --- a/src/wren_vm.c +++ b/src/wren_vm.c @@ -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 diff --git a/src/wren_vm.h b/src/wren_vm.h index 3850fae5..50a41545 100644 --- a/src/wren_vm.h +++ b/src/wren_vm.h @@ -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,