forked from Mirror/wren
While statements.
This commit is contained in:
@ -46,6 +46,7 @@ typedef enum
|
||||
TOKEN_THIS,
|
||||
TOKEN_TRUE,
|
||||
TOKEN_VAR,
|
||||
TOKEN_WHILE,
|
||||
|
||||
TOKEN_NAME,
|
||||
TOKEN_NUMBER,
|
||||
@ -296,6 +297,7 @@ static void readName(Parser* parser)
|
||||
if (isKeyword(parser, "this")) type = TOKEN_THIS;
|
||||
if (isKeyword(parser, "true")) type = TOKEN_TRUE;
|
||||
if (isKeyword(parser, "var")) type = TOKEN_VAR;
|
||||
if (isKeyword(parser, "while")) type = TOKEN_WHILE;
|
||||
|
||||
makeToken(parser, type);
|
||||
}
|
||||
@ -489,6 +491,7 @@ static void nextToken(Parser* parser)
|
||||
case TOKEN_IS:
|
||||
case TOKEN_STATIC:
|
||||
case TOKEN_VAR:
|
||||
case TOKEN_WHILE:
|
||||
parser->skipNewlines = 1;
|
||||
|
||||
// Emit this token.
|
||||
@ -1032,6 +1035,7 @@ GrammarRule rules[] =
|
||||
/* TOKEN_THIS */ PREFIX(this_),
|
||||
/* TOKEN_TRUE */ PREFIX(boolean),
|
||||
/* TOKEN_VAR */ UNUSED,
|
||||
/* TOKEN_WHILE */ UNUSED,
|
||||
/* TOKEN_NAME */ { name, NULL, parameterList, PREC_NONE, NULL },
|
||||
/* TOKEN_NUMBER */ PREFIX(number),
|
||||
/* TOKEN_STRING */ PREFIX(string),
|
||||
@ -1079,6 +1083,13 @@ void assignment(Compiler* compiler)
|
||||
expression(compiler, 1);
|
||||
}
|
||||
|
||||
// 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.
|
||||
static void patchJump(Compiler* compiler, int offset)
|
||||
{
|
||||
compiler->fn->bytecode[offset] = compiler->numCodes - offset - 1;
|
||||
}
|
||||
|
||||
// Parses a "statement": any expression including expressions like variable
|
||||
// declarations which can only appear at the top level of a block.
|
||||
void statement(Compiler* compiler)
|
||||
@ -1091,22 +1102,18 @@ void statement(Compiler* compiler)
|
||||
assignment(compiler);
|
||||
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after if condition.");
|
||||
|
||||
// Compile the then branch.
|
||||
// Jump to the else branch if the condition is false.
|
||||
emit(compiler, CODE_JUMP_IF);
|
||||
|
||||
// Emit a placeholder. We'll patch it when we know what to jump to.
|
||||
int ifJump = emit(compiler, 255);
|
||||
|
||||
// Compile the then branch.
|
||||
statement(compiler);
|
||||
|
||||
// Jump over the else branch when the if branch is taken.
|
||||
emit(compiler, CODE_JUMP);
|
||||
|
||||
// Emit a placeholder. We'll patch it when we know what to jump to.
|
||||
int elseJump = emit(compiler, 255);
|
||||
|
||||
// Patch the jump.
|
||||
compiler->fn->bytecode[ifJump] = compiler->numCodes - ifJump - 1;
|
||||
patchJump(compiler, ifJump);
|
||||
|
||||
// Compile the else branch if there is one.
|
||||
if (match(compiler, TOKEN_ELSE))
|
||||
@ -1120,7 +1127,36 @@ void statement(Compiler* compiler)
|
||||
}
|
||||
|
||||
// Patch the jump over the else.
|
||||
compiler->fn->bytecode[elseJump] = compiler->numCodes - elseJump - 1;
|
||||
patchJump(compiler, elseJump);
|
||||
return;
|
||||
}
|
||||
|
||||
if (match(compiler, TOKEN_WHILE))
|
||||
{
|
||||
// Remember what instruction to loop back to.
|
||||
int loopStart = compiler->numCodes - 1;
|
||||
|
||||
// Compile the condition.
|
||||
consume(compiler, TOKEN_LEFT_PAREN, "Expect '(' after 'while'.");
|
||||
assignment(compiler);
|
||||
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after while condition.");
|
||||
|
||||
emit(compiler, CODE_JUMP_IF);
|
||||
int exitJump = emit(compiler, 255);
|
||||
|
||||
// Compile the body.
|
||||
statement(compiler);
|
||||
|
||||
// Loop back to the top.
|
||||
emit(compiler, CODE_LOOP);
|
||||
int loopOffset = compiler->numCodes - loopStart;
|
||||
emit(compiler, loopOffset);
|
||||
|
||||
patchJump(compiler, exitJump);
|
||||
|
||||
// A while loop always evaluates to null.
|
||||
// TODO(bob): Is there a more useful value it could return?
|
||||
emit(compiler, CODE_NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
339
src/vm.c
339
src/vm.c
@ -418,159 +418,176 @@ Value findGlobal(VM* vm, const char* name)
|
||||
return vm->globals[symbol];
|
||||
}
|
||||
|
||||
/*
|
||||
int dumpInstruction(VM* vm, ObjFn* fn, int i)
|
||||
{
|
||||
int start = i;
|
||||
unsigned char* bytecode = fn->bytecode;
|
||||
unsigned char code = bytecode[i++];
|
||||
|
||||
printf("%04d ", i);
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case CODE_CONSTANT:
|
||||
{
|
||||
int constant = bytecode[i++];
|
||||
printf("CONSTANT ");
|
||||
printValue(fn->constants[constant]);
|
||||
printf("\n");
|
||||
printf("%04d | constant %d\n", i, constant);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_NULL:
|
||||
printf("NULL\n");
|
||||
break;
|
||||
|
||||
case CODE_FALSE:
|
||||
printf("FALSE\n");
|
||||
break;
|
||||
|
||||
case CODE_TRUE:
|
||||
printf("TRUE\n");
|
||||
break;
|
||||
|
||||
case CODE_CLASS:
|
||||
printf("CLASS\n");
|
||||
break;
|
||||
|
||||
case CODE_SUBCLASS:
|
||||
printf("SUBCLASS\n");
|
||||
break;
|
||||
|
||||
case CODE_METACLASS:
|
||||
printf("METACLASS\n");
|
||||
break;
|
||||
|
||||
case CODE_METHOD:
|
||||
{
|
||||
int symbol = bytecode[i++];
|
||||
int constant = bytecode[i++];
|
||||
printf("METHOD \"%s\"\n", getSymbolName(&vm->methods, symbol));
|
||||
printf("%04d | symbol %d\n", i - 1, symbol);
|
||||
printf("%04d | constant %d\n", i, constant);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LOAD_LOCAL:
|
||||
{
|
||||
int local = bytecode[i++];
|
||||
printf("LOAD_LOCAL %d\n", local);
|
||||
printf("%04d | local %d\n", i, local);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_STORE_LOCAL:
|
||||
{
|
||||
int local = bytecode[i++];
|
||||
printf("STORE_LOCAL %d\n", local);
|
||||
printf("%04d | local %d\n", i, local);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LOAD_GLOBAL:
|
||||
{
|
||||
int global = bytecode[i++];
|
||||
printf("LOAD_GLOBAL \"%s\"\n",
|
||||
getSymbolName(&vm->globalSymbols, global));
|
||||
printf("%04d | global %d\n", i, global);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_STORE_GLOBAL:
|
||||
{
|
||||
int global = bytecode[i++];
|
||||
printf("STORE_GLOBAL \"%s\"\n",
|
||||
getSymbolName(&vm->globalSymbols, global));
|
||||
printf("%04d | global %d\n", i, global);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_DUP:
|
||||
printf("DUP\n");
|
||||
break;
|
||||
|
||||
case CODE_POP:
|
||||
printf("POP\n");
|
||||
break;
|
||||
|
||||
case CODE_CALL_0:
|
||||
case CODE_CALL_1:
|
||||
case CODE_CALL_2:
|
||||
case CODE_CALL_3:
|
||||
case CODE_CALL_4:
|
||||
case CODE_CALL_5:
|
||||
case CODE_CALL_6:
|
||||
case CODE_CALL_7:
|
||||
case CODE_CALL_8:
|
||||
case CODE_CALL_9:
|
||||
case CODE_CALL_10:
|
||||
{
|
||||
// Add one for the implicit receiver argument.
|
||||
int numArgs = bytecode[i - 1] - CODE_CALL_0;
|
||||
int symbol = bytecode[i++];
|
||||
printf("CALL_%d \"%s\"\n", numArgs,
|
||||
getSymbolName(&vm->methods, symbol));
|
||||
printf("%04d | symbol %d\n", i, symbol);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_JUMP:
|
||||
{
|
||||
int offset = bytecode[i++];
|
||||
printf("JUMP %d\n", offset);
|
||||
printf("%04d | offset %d\n", i, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LOOP:
|
||||
{
|
||||
int offset = bytecode[i++];
|
||||
printf("LOOP %d\n", offset);
|
||||
printf("%04d | offset -%d\n", i, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_JUMP_IF:
|
||||
{
|
||||
int offset = bytecode[i++];
|
||||
printf("JUMP_IF %d\n", offset);
|
||||
printf("%04d | offset %d\n", i, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_IS:
|
||||
printf("CODE_IS\n");
|
||||
break;
|
||||
|
||||
case CODE_END:
|
||||
printf("CODE_END\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("UKNOWN! [%d]\n", bytecode[i - 1]);
|
||||
break;
|
||||
}
|
||||
|
||||
// Return how many bytes this instruction takes, or -1 if it's an END.
|
||||
if (code == CODE_END) return -1;
|
||||
return i - start;
|
||||
}
|
||||
|
||||
// TODO(bob): For debugging. Move to separate module.
|
||||
void dumpCode(VM* vm, ObjFn* fn)
|
||||
{
|
||||
unsigned char* bytecode = fn->bytecode;
|
||||
int done = 0;
|
||||
int i = 0;
|
||||
while (!done)
|
||||
for (;;)
|
||||
{
|
||||
printf("%04d ", i);
|
||||
unsigned char code = bytecode[i++];
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case CODE_CONSTANT:
|
||||
{
|
||||
int constant = bytecode[i++];
|
||||
printf("CONSTANT ");
|
||||
printValue(fn->constants[constant]);
|
||||
printf("\n");
|
||||
printf("%04d | constant %d\n", i - 1, constant);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_NULL:
|
||||
printf("NULL\n");
|
||||
break;
|
||||
|
||||
case CODE_FALSE:
|
||||
printf("FALSE\n");
|
||||
break;
|
||||
|
||||
case CODE_TRUE:
|
||||
printf("TRUE\n");
|
||||
break;
|
||||
|
||||
case CODE_CLASS:
|
||||
printf("CLASS\n");
|
||||
break;
|
||||
|
||||
case CODE_SUBCLASS:
|
||||
printf("SUBCLASS\n");
|
||||
break;
|
||||
|
||||
case CODE_METACLASS:
|
||||
printf("METACLASS\n");
|
||||
break;
|
||||
|
||||
case CODE_METHOD:
|
||||
{
|
||||
int symbol = bytecode[i++];
|
||||
int constant = bytecode[i++];
|
||||
printf("METHOD \"%s\"\n", getSymbolName(&vm->methods, symbol));
|
||||
printf("%04d | symbol %d\n", i - 2, symbol);
|
||||
printf("%04d | constant %d\n", i - 1, constant);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LOAD_LOCAL:
|
||||
{
|
||||
int local = bytecode[i++];
|
||||
printf("LOAD_LOCAL %d\n", local);
|
||||
printf("%04d | local %d\n", i - 1, local);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_STORE_LOCAL:
|
||||
{
|
||||
int local = bytecode[i++];
|
||||
printf("STORE_LOCAL %d\n", local);
|
||||
printf("%04d | local %d\n", i - 1, local);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LOAD_GLOBAL:
|
||||
{
|
||||
int global = bytecode[i++];
|
||||
printf("LOAD_GLOBAL \"%s\"\n",
|
||||
getSymbolName(&vm->globalSymbols, global));
|
||||
printf("%04d | global %d\n", i - 1, global);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_STORE_GLOBAL:
|
||||
{
|
||||
int global = bytecode[i++];
|
||||
printf("STORE_GLOBAL \"%s\"\n",
|
||||
getSymbolName(&vm->globalSymbols, global));
|
||||
printf("%04d | global %d\n", i - 1, global);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_DUP:
|
||||
printf("DUP\n");
|
||||
break;
|
||||
|
||||
case CODE_POP:
|
||||
printf("POP\n");
|
||||
break;
|
||||
|
||||
case CODE_CALL_0:
|
||||
case CODE_CALL_1:
|
||||
case CODE_CALL_2:
|
||||
case CODE_CALL_3:
|
||||
case CODE_CALL_4:
|
||||
case CODE_CALL_5:
|
||||
case CODE_CALL_6:
|
||||
case CODE_CALL_7:
|
||||
case CODE_CALL_8:
|
||||
case CODE_CALL_9:
|
||||
case CODE_CALL_10:
|
||||
{
|
||||
// Add one for the implicit receiver argument.
|
||||
int numArgs = bytecode[i - 1] - CODE_CALL_0;
|
||||
int symbol = bytecode[i++];
|
||||
printf("CALL_%d \"%s\"\n", numArgs,
|
||||
getSymbolName(&vm->methods, symbol));
|
||||
printf("%04d | symbol %d\n", i - 1, symbol);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_JUMP:
|
||||
{
|
||||
int offset = bytecode[i++];
|
||||
printf("JUMP %d\n", offset);
|
||||
printf("%04d | offset %d\n", i - 1, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_JUMP_IF:
|
||||
{
|
||||
int offset = bytecode[i++];
|
||||
printf("JUMP_IF %d\n", offset);
|
||||
printf("%04d | offset %d\n", i - 1, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_IS:
|
||||
printf("CODE_IS\n");
|
||||
break;
|
||||
|
||||
case CODE_END:
|
||||
printf("CODE_END\n");
|
||||
done = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("[%d]\n", bytecode[i - 1]);
|
||||
break;
|
||||
}
|
||||
int offset = dumpInstruction(vm, fn, i);
|
||||
if (offset == -1) break;
|
||||
i += offset;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Returns the class of [object].
|
||||
static ObjClass* getClass(VM* vm, Value value)
|
||||
@ -619,14 +636,23 @@ static ObjClass* getClass(VM* vm, Value value)
|
||||
#endif
|
||||
}
|
||||
|
||||
void dumpStack(Fiber* fiber)
|
||||
{
|
||||
printf(":: ");
|
||||
for (int i = 0; i < fiber->stackSize; i++)
|
||||
{
|
||||
printValue(fiber->stack[i]);
|
||||
printf(" | ");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
Value interpret(VM* vm, ObjFn* fn)
|
||||
{
|
||||
Fiber* fiber = vm->fiber;
|
||||
|
||||
callFunction(fiber, fn, 0);
|
||||
|
||||
// These macros are designed to only be invoked within this function.
|
||||
|
||||
// TODO(bob): Check for stack overflow.
|
||||
#define PUSH(value) (fiber->stack[fiber->stackSize++] = value)
|
||||
#define POP() (fiber->stack[--fiber->stackSize])
|
||||
@ -813,9 +839,22 @@ Value interpret(VM* vm, ObjFn* fn)
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_JUMP:{
|
||||
case CODE_JUMP:
|
||||
{
|
||||
int offset = READ_ARG();
|
||||
ip+= offset;
|
||||
ip += offset;
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LOOP:
|
||||
{
|
||||
// The loop body's result is on the top of the stack. Since we are
|
||||
// looping and running the body again, discard it.
|
||||
POP();
|
||||
|
||||
// Jump back to the top of the loop.
|
||||
int offset = READ_ARG();
|
||||
ip -= offset;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
5
src/vm.h
5
src/vm.h
@ -1,6 +1,7 @@
|
||||
#ifndef wren_vm_h
|
||||
#define wren_vm_h
|
||||
|
||||
#include "common.h"
|
||||
#include "value.h"
|
||||
|
||||
// TODO(bob): Make these externally controllable.
|
||||
@ -71,6 +72,10 @@ typedef enum
|
||||
// Jump the instruction pointer [arg1] forward.
|
||||
CODE_JUMP,
|
||||
|
||||
// Jump the instruction pointer [arg1] backward. Pop and discard the top of
|
||||
// the stack.
|
||||
CODE_LOOP,
|
||||
|
||||
// Pop and if not truthy then jump the instruction pointer [arg1] forward.
|
||||
CODE_JUMP_IF,
|
||||
|
||||
|
||||
@ -28,3 +28,11 @@ io.write(c) // expect: good
|
||||
|
||||
// Assignment in if condition.
|
||||
if (a = true) io.write(a) // expect: true
|
||||
|
||||
// Newline after "if".
|
||||
if
|
||||
(true) io.write("good") // expect: good
|
||||
|
||||
// Newline after "else".
|
||||
if (false) io.write("bad") else
|
||||
io.write("good") // expect: good
|
||||
31
test/while.wren
Normal file
31
test/while.wren
Normal file
@ -0,0 +1,31 @@
|
||||
// Single-expression body.
|
||||
var c = 0
|
||||
while (c < 3) io.write(c = c + 1)
|
||||
// expect: 1
|
||||
// expect: 2
|
||||
// expect: 3
|
||||
|
||||
// Block body.
|
||||
var a = 0
|
||||
while (a < 3) {
|
||||
io.write(a)
|
||||
a = a + 1
|
||||
}
|
||||
// expect: 0
|
||||
// expect: 1
|
||||
// expect: 2
|
||||
|
||||
// Newline after "while".
|
||||
var d = 0
|
||||
while
|
||||
(d < 3) io.write(d = d + 1)
|
||||
// expect: 1
|
||||
// expect: 2
|
||||
// expect: 3
|
||||
|
||||
// Result is null.
|
||||
var e = 0
|
||||
var f = while (e < 3) {
|
||||
e = e + 1
|
||||
}
|
||||
io.write(f) // expect: null
|
||||
Reference in New Issue
Block a user