1
0
forked from Mirror/wren

While statements.

This commit is contained in:
Bob Nystrom
2013-11-17 22:38:59 -08:00
parent 64509a4f61
commit 4d23d58bf1
5 changed files with 277 additions and 158 deletions

View File

@ -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
View File

@ -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;
}

View File

@ -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,

View File

@ -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
View 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