Merge branch 'cpp'
Conflicts: src/wren_vm.c
This commit is contained in:
commit
8651d9c12a
2
.gitignore
vendored
2
.gitignore
vendored
@ -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/
|
||||
|
||||
46
Makefile
46
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)
|
||||
|
||||
@ -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].
|
||||
|
||||
@ -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].
|
||||
|
||||
@ -42,7 +42,7 @@ else:
|
||||
passed = 0
|
||||
failed = 0
|
||||
skipped = defaultdict(int)
|
||||
num_skipped = 0;
|
||||
num_skipped = 0
|
||||
|
||||
|
||||
def walk(dir, callback):
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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';
|
||||
|
||||
|
||||
@ -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; \
|
||||
} \
|
||||
|
||||
@ -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("[");
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user