1
0
forked from Mirror/wren

Start hacking on "if" expressions.

This commit is contained in:
Bob Nystrom
2013-11-05 15:40:21 -08:00
parent 67356df1f9
commit 3db228eecf
6 changed files with 303 additions and 8 deletions

View File

@ -35,7 +35,9 @@ typedef enum
TOKEN_BANGEQ,
TOKEN_CLASS,
TOKEN_ELSE,
TOKEN_FALSE,
TOKEN_IF,
TOKEN_META,
TOKEN_TRUE,
TOKEN_VAR,
@ -149,7 +151,7 @@ static void storeVariable(Compiler* compiler, int symbol);
static int internSymbol(Compiler* compiler);
// Emits one bytecode instruction or argument.
static void emit(Compiler* compiler, Code code);
static int emit(Compiler* compiler, Code code);
// Outputs a compile or syntax error.
static void error(Compiler* compiler, const char* format, ...);
@ -253,7 +255,9 @@ ParseRule rules[] =
/* TOKEN_EQEQ */ INFIX_OPERATOR(PREC_EQUALITY, "== "),
/* TOKEN_BANGEQ */ INFIX_OPERATOR(PREC_EQUALITY, "!= "),
/* TOKEN_CLASS */ UNUSED,
/* TOKEN_ELSE */ UNUSED,
/* TOKEN_FALSE */ PREFIX(boolean),
/* TOKEN_IF */ UNUSED,
/* TOKEN_META */ UNUSED,
/* TOKEN_TRUE */ PREFIX(boolean),
/* TOKEN_VAR */ UNUSED,
@ -385,9 +389,10 @@ int internSymbol(Compiler* compiler)
compiler->parser->previous.end - compiler->parser->previous.start);
}
void emit(Compiler* compiler, Code code)
int emit(Compiler* compiler, Code code)
{
compiler->block->bytecode[compiler->numCodes++] = code;
return compiler->numCodes - 1;
}
void error(Compiler* compiler, const char* format, ...)
@ -469,6 +474,48 @@ void statement(Compiler* compiler)
void expression(Compiler* compiler)
{
if (match(compiler, TOKEN_IF))
{
// Compile the condition.
consume(compiler, TOKEN_LEFT_PAREN);
expression(compiler);
consume(compiler, TOKEN_RIGHT_PAREN);
// TODO(bob): Block bodies.
// Compile the then branch.
emit(compiler, CODE_JUMP_IF);
// Emit a placeholder. We'll patch it when we know what to jump to.
int ifJump = emit(compiler, 255);
expression(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->block->bytecode[ifJump] = compiler->numCodes - ifJump - 1;
// Compile the else branch if there is one.
if (match(compiler, TOKEN_ELSE))
{
// TODO(bob): Block bodies.
expression(compiler);
}
else
{
// Just default to null.
emit(compiler, CODE_NULL);
}
// Patch the jump over the else.
compiler->block->bytecode[elseJump] = compiler->numCodes - elseJump - 1;
return;
}
return parsePrecedence(compiler, PREC_LOWEST);
}
@ -751,6 +798,8 @@ void nextToken(Parser* parser)
case TOKEN_EQEQ:
case TOKEN_BANGEQ:
case TOKEN_CLASS:
case TOKEN_ELSE:
case TOKEN_IF:
case TOKEN_META:
case TOKEN_VAR:
parser->skipNewlines = 1;
@ -899,7 +948,9 @@ void readName(Parser* parser)
TokenType type = TOKEN_NAME;
if (isKeyword(parser, "class")) type = TOKEN_CLASS;
if (isKeyword(parser, "if")) type = TOKEN_IF;
if (isKeyword(parser, "false")) type = TOKEN_FALSE;
if (isKeyword(parser, "else")) type = TOKEN_ELSE;
if (isKeyword(parser, "meta")) type = TOKEN_META;
if (isKeyword(parser, "true")) type = TOKEN_TRUE;
if (isKeyword(parser, "var")) type = TOKEN_VAR;

View File

@ -227,6 +227,8 @@ void registerPrimitives(VM* vm)
vm->classClass = makeClass();
vm->nullClass = makeClass();
vm->numClass = makeClass();
PRIMITIVE(vm->numClass, "abs", num_abs);
PRIMITIVE(vm->numClass, "toString", num_toString)
@ -257,4 +259,4 @@ void registerPrimitives(VM* vm)
// TODO(bob): Make this a distinct object type.
vm->unsupported = (Value)makeInstance(unsupportedClass);
}
}

189
src/vm.c
View File

@ -81,6 +81,14 @@ ObjInstance* makeInstance(ObjClass* classObj)
return instance;
}
Value makeNull()
{
Obj* obj = malloc(sizeof(Obj));
obj->type = OBJ_NULL;
obj->flags = 0;
return obj;
}
ObjNum* makeNum(double number)
{
ObjNum* num = malloc(sizeof(ObjNum));
@ -157,6 +165,147 @@ const char* getSymbolName(SymbolTable* symbols, int symbol)
return symbols->names[symbol];
}
// TODO(bob): For debugging. Move to separate module.
/*
void dumpCode(ObjBlock* block)
{
unsigned char* bytecode = block->bytecode;
int done = 0;
int i = 0;
while (!done)
{
printf("%04d ", i);
unsigned char code = bytecode[i++];
switch (code)
{
case CODE_CONSTANT:
{
int constant = bytecode[i++];
printf("CONSTANT %d (", constant);
printValue(block->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_METHOD:
{
int symbol = bytecode[i++];
int constant = bytecode[i++];
printf("METHOD symbol %d constant %d\n", symbol, constant);
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 %d\n", global);
printf("%04d (global %d)\n", i - 1, global);
break;
}
case CODE_STORE_GLOBAL:
{
int global = bytecode[i++];
printf("STORE_GLOBAL %d\n", 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 %d\n", numArgs, 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_END:
{
printf("CODE_END\n");
done = 1;
break;
}
default:
printf("[%d]\n", bytecode[i - 1]);
break;
}
}
}
*/
Value interpret(VM* vm, ObjBlock* block)
{
Fiber fiber;
@ -179,6 +328,10 @@ Value interpret(VM* vm, ObjBlock* block)
break;
}
case CODE_NULL:
push(&fiber, makeNull());
break;
case CODE_FALSE:
push(&fiber, makeBool(0));
break;
@ -284,6 +437,10 @@ Value interpret(VM* vm, ObjBlock* block)
classObj = vm->boolClass;
break;
case OBJ_NULL:
classObj = vm->nullClass;
break;
case OBJ_NUM:
classObj = vm->numClass;
break;
@ -333,6 +490,26 @@ Value interpret(VM* vm, ObjBlock* block)
break;
}
case CODE_JUMP:
{
int offset = frame->block->bytecode[frame->ip++];
frame->ip += offset;
break;
}
case CODE_JUMP_IF:
{
int offset = frame->block->bytecode[frame->ip++];
Value condition = pop(&fiber);
// False is the only falsey value.
if (condition->type == OBJ_FALSE)
{
frame->ip += offset;
}
break;
}
case CODE_END:
{
Value result = pop(&fiber);
@ -379,18 +556,22 @@ void printValue(Value value)
printf("[block]");
break;
case OBJ_FALSE:
printf("false");
break;
case OBJ_CLASS:
printf("[class]");
break;
case OBJ_FALSE:
printf("false");
break;
case OBJ_INSTANCE:
printf("[instance]");
break;
case OBJ_NULL:
printf("null");
break;
case OBJ_NUM:
printf("%g", ((ObjNum*)value)->value);
break;

View File

@ -23,6 +23,7 @@ typedef enum {
OBJ_CLASS,
OBJ_FALSE,
OBJ_INSTANCE,
OBJ_NULL,
OBJ_NUM,
OBJ_STRING,
OBJ_TRUE
@ -105,6 +106,9 @@ typedef enum
// Load the constant at index [arg].
CODE_CONSTANT,
// Push null onto the stack.
CODE_NULL,
// Push false onto the stack.
CODE_FALSE,
@ -149,7 +153,13 @@ typedef enum
CODE_CALL_8,
CODE_CALL_9,
CODE_CALL_10,
// Jump the instruction pointer [arg1] forward.
CODE_JUMP,
// Pop and if not truthy then jump the instruction pointer [arg1] forward.
CODE_JUMP_IF,
// The current block is done and should be exited.
CODE_END
} Code;
@ -168,6 +178,7 @@ struct sVM
ObjClass* blockClass;
ObjClass* boolClass;
ObjClass* classClass;
ObjClass* nullClass;
ObjClass* numClass;
ObjClass* stringClass;
@ -218,6 +229,9 @@ ObjClass* makeClass();
// Creates a new instance of the given [classObj].
ObjInstance* makeInstance(ObjClass* classObj);
// Creates a new null object.
Value makeNull();
// Creates a new number object.
ObjNum* makeNum(double number);

31
test/block_syntax.wren Normal file
View File

@ -0,0 +1,31 @@
// Single line.
{ io.write("ok") }.call // expect: ok
// No trailing newline.
{
io.write("ok") }.call // expect: ok
// Trailing newline.
{
io.write("ok") // expect: ok
}.call
// Multiple expressions.
{
io.write("1") // expect: 1
io.write("2") // expect: 2
}.call
// Extra newlines.
{
io.write("1") // expect: 1
io.write("2") // expect: 2
}.call
// TODO(bob): Arguments.

16
test/if_syntax.wren Normal file
View File

@ -0,0 +1,16 @@
// Evaluate the 'then' expression if the condition is true.
if (true) io.write("good") // expect: good
if (false) io.write("bad")
// Evaluate the 'else' expression if the condition is false.
if (true) io.write("good") else io.write("bad") // expect: good
if (false) io.write("bad") else io.write("good") // expect: good
// Return the 'then' expression if the condition is true.
io.write(if (true) "good") // expect: good
// Return null if the condition is false and there is no else.
io.write(if (false) "bad") // expect: null
// Return the 'else' expression if the condition is false.
io.write(if (false) "bad" else "good") // expect: good