Start working on class definitions.

This commit is contained in:
Bob Nystrom
2013-10-23 22:50:04 -07:00
parent 40e6d2f077
commit 67e5660346
6 changed files with 181 additions and 71 deletions

View File

@ -1,3 +1,4 @@
class Foo {}
var a = 123
var b = 345
a

View File

@ -32,6 +32,8 @@ typedef enum
TOKEN_EQEQ,
TOKEN_BANGEQ,
TOKEN_CLASS,
TOKEN_META,
TOKEN_VAR,
TOKEN_NAME,
@ -73,7 +75,7 @@ typedef struct
Token previous;
// The block being compiled.
Block* block;
ObjBlock* block;
int numCodes;
// Symbol table for declared local variables in the current block.
@ -111,7 +113,7 @@ static void makeToken(Compiler* compiler, TokenType type);
static void emit(Compiler* compiler, Code code);
static void error(Compiler* compiler, const char* format, ...);
Block* compile(VM* vm, const char* source, size_t sourceLength)
ObjBlock* compile(VM* vm, const char* source, size_t sourceLength)
{
Compiler compiler;
compiler.vm = vm;
@ -133,7 +135,7 @@ Block* compile(VM* vm, const char* source, size_t sourceLength)
// Read the first token.
advance(&compiler);
compiler.block = malloc(sizeof(Block));
compiler.block = makeBlock();
// TODO(bob): Hack! make variable sized.
compiler.block->bytecode = malloc(sizeof(Code) * 1024);
@ -147,6 +149,8 @@ Block* compile(VM* vm, const char* source, size_t sourceLength)
{
statement(&compiler);
consume(&compiler, TOKEN_LINE);
if (match(&compiler, TOKEN_EOF)) break;
// Discard the result of the previous expression.
@ -162,6 +166,36 @@ Block* compile(VM* vm, const char* source, size_t sourceLength)
void statement(Compiler* compiler)
{
if (match(compiler, TOKEN_CLASS))
{
consume(compiler, TOKEN_NAME);
// TODO(bob): Copied from below. Unify.
int local = addSymbol(&compiler->locals,
compiler->source + compiler->previous.start,
compiler->previous.end - compiler->previous.start);
if (local == -1)
{
error(compiler, "Local variable is already defined.");
}
// Create the empty class.
emit(compiler, CODE_CLASS);
// Store it in its name.
emit(compiler, CODE_STORE_LOCAL);
emit(compiler, local);
// Compile the method definitions.
consume(compiler, TOKEN_LEFT_BRACE);
// TODO(bob): Definitions!
consume(compiler, TOKEN_RIGHT_BRACE);
return;
}
if (match(compiler, TOKEN_VAR))
{
consume(compiler, TOKEN_NAME);
@ -187,7 +221,6 @@ void statement(Compiler* compiler)
// Statement expression.
expression(compiler);
consume(compiler, TOKEN_LINE);
}
void expression(Compiler* compiler)
@ -245,6 +278,7 @@ void primary(Compiler* compiler)
void number(Compiler* compiler, Token* token)
{
char* end;
// TODO(bob): Parse actual double!
long value = strtol(compiler->source + token->start, &end, 10);
// TODO(bob): Check errno == ERANGE here.
if (end == compiler->source + token->start)
@ -255,8 +289,7 @@ void number(Compiler* compiler, Token* token)
// Define a constant for the literal.
// TODO(bob): See if constant with same value already exists.
// TODO(bob): Handle truncation!
Value constant = makeNum((int)value);
Value constant = (Value)makeNum((double)value);
compiler->block->constants[compiler->block->numConstants++] = constant;
@ -413,6 +446,8 @@ void readName(Compiler* compiler)
TokenType type = TOKEN_NAME;
if (isKeyword(compiler, "class")) type = TOKEN_CLASS;
if (isKeyword(compiler, "meta")) type = TOKEN_META;
if (isKeyword(compiler, "var")) type = TOKEN_VAR;
makeToken(compiler, type);

View File

@ -3,6 +3,6 @@
#include "vm.h"
Block* compile(VM* vm, const char* source, size_t sourceLength);
ObjBlock* compile(VM* vm, const char* source, size_t sourceLength);
#endif

View File

@ -26,7 +26,7 @@ int main(int argc, const char * argv[])
size_t length;
char* source = readFile(argv[1], &length);
VM* vm = newVM();
Block* block = compile(vm, source, length);
ObjBlock* block = compile(vm, source, length);
Value value = interpret(vm, block);
printValue(value);
printf("\n");

111
src/vm.c
View File

@ -11,7 +11,7 @@ typedef struct
int ip;
// The block being executed.
Block* block;
ObjBlock* block;
// Index of the stack slot that contains the first local for this block.
int locals;
@ -26,7 +26,7 @@ typedef struct
int numFrames;
} Fiber;
static void callBlock(Fiber* fiber, Block* block, int locals);
static void callBlock(Fiber* fiber, ObjBlock* block, int locals);
static void push(Fiber* fiber, Value value);
static Value pop(Fiber* fiber);
static Value primitiveNumAbs(Value number);
@ -36,12 +36,10 @@ VM* newVM()
VM* vm = malloc(sizeof(VM));
initSymbolTable(&vm->symbols);
for (int i = 0; i < MAX_SYMBOLS; i++)
{
vm->numClass.methods[i] = NULL;
}
vm->numClass.methods[ensureSymbol(&vm->symbols, "abs", 3)] = primitiveNumAbs;
vm->numClass = makeClass();
int symbol = ensureSymbol(&vm->symbols, "abs", 3);
vm->numClass->methods[symbol].type = METHOD_PRIMITIVE;
vm->numClass->methods[symbol].primitive = primitiveNumAbs;
return vm;
}
@ -52,14 +50,35 @@ void freeVM(VM* vm)
free(vm);
}
Value makeNum(int number)
ObjClass* makeClass()
{
Value value = malloc(sizeof(Obj));
value->type = OBJ_INT;
value->flags = 0;
value->value = number;
ObjClass* obj = malloc(sizeof(ObjClass));
obj->obj.type = OBJ_CLASS;
obj->obj.flags = 0;
return value;
for (int i = 0; i < MAX_SYMBOLS; i++)
{
obj->methods[i].type = METHOD_NONE;
}
return obj;
}
ObjBlock* makeBlock()
{
ObjBlock* block = malloc(sizeof(ObjBlock));
block->obj.type = OBJ_NUM;
block->obj.flags = 0;
return block;
}
ObjNum* makeNum(double number)
{
ObjNum* num = malloc(sizeof(ObjNum));
num->obj.type = OBJ_NUM;
num->obj.flags = 0;
num->value = number;
return num;
}
void initSymbolTable(SymbolTable* symbols)
@ -115,7 +134,7 @@ int findSymbol(SymbolTable* symbols, const char* name, size_t length)
return -1;
}
Value interpret(VM* vm, Block* block)
Value interpret(VM* vm, ObjBlock* block)
{
Fiber fiber;
fiber.stackSize = 0;
@ -137,21 +156,34 @@ Value interpret(VM* vm, Block* block)
break;
}
case CODE_CLASS:
push(&fiber, (Value)makeClass());
printf("push class at %d\n", fiber.stackSize - 1);
break;
case CODE_LOAD_LOCAL:
{
int local = frame->block->bytecode[frame->ip++];
push(&fiber, fiber.stack[frame->locals + local]);
printf("load local %d to %d\n", local, fiber.stackSize - 1);
break;
}
case CODE_STORE_LOCAL:
{
int local = frame->block->bytecode[frame->ip++];
fiber.stack[frame->locals + local] = pop(&fiber);
printf("store local %d from %d\n", local, fiber.stackSize - 1);
fiber.stack[frame->locals + local] = fiber.stack[fiber.stackSize - 1];
break;
}
case CODE_DUP:
push(&fiber, fiber.stack[fiber.stackSize - 1]);
printf("dup %d\n", fiber.stackSize - 1);
break;
case CODE_POP:
printf("pop %d\n", fiber.stackSize - 1);
pop(&fiber);
break;
@ -163,19 +195,24 @@ Value interpret(VM* vm, Block* block)
int symbol = frame->block->bytecode[frame->ip++];
// TODO(bob): Support classes for other object types.
Class* classObj = &vm->numClass;
ObjClass* classObj = vm->numClass;
Method* method = &classObj->methods[symbol];
switch (method->type)
{
case METHOD_NONE:
// TODO(bob): Should return nil or suspend fiber or something.
printf("No method.\n");
exit(1);
break;
Primitive primitive = classObj->methods[symbol];
if (primitive)
{
Value result = primitive(receiver);
push(&fiber, result);
}
else
{
// TODO(bob): Should return nil or suspend fiber or something.
printf("No method.\n");
exit(1);
case METHOD_PRIMITIVE:
push(&fiber, method->primitive(receiver));
break;
case METHOD_BLOCK:
// TODO(bob): Should pass in correct index for locals.
callBlock(&fiber, method->block, fiber.stackSize);
break;
}
break;
}
@ -200,13 +237,21 @@ void printValue(Value value)
{
switch (value->type)
{
case OBJ_INT:
printf("%d", value->value);
case OBJ_NUM:
printf("%f", ((ObjNum*)value)->value);
break;
case OBJ_BLOCK:
printf("[block]");
break;
case OBJ_CLASS:
printf("[class]");
break;
}
}
void callBlock(Fiber* fiber, Block* block, int locals)
void callBlock(Fiber* fiber, ObjBlock* block, int locals)
{
fiber->frames[fiber->numFrames].block = block;
fiber->frames[fiber->numFrames].ip = 0;
@ -227,8 +272,8 @@ Value pop(Fiber* fiber)
Value primitiveNumAbs(Value number)
{
int value = number->value;
double value = ((ObjNum*)number)->value;
if (value < 0) value = -value;
return makeNum(value);
return (Value)makeNum(value);
}

View File

@ -4,10 +4,12 @@
// TODO(bob): Make these externally controllable.
#define STACK_SIZE 1024
#define MAX_CALL_FRAMES 256
#define MAX_SYMBOLS 256
typedef enum {
OBJ_INT
OBJ_NUM,
OBJ_BLOCK,
OBJ_CLASS
} ObjType;
typedef enum
@ -16,23 +18,66 @@ typedef enum
FLAG_MARKED = 0x01,
} ObjFlags;
typedef struct sObj {
typedef struct
{
ObjType type;
ObjFlags flags;
union {
/* OBJ_INT */
int value;
};
} Obj;
typedef Obj* Value;
typedef struct
{
Obj obj;
double value;
} ObjNum;
typedef struct
{
Obj obj;
unsigned char* bytecode;
Value* constants;
int numConstants;
int numLocals;
} ObjBlock;
typedef Value (*Primitive)(Value receiver);
typedef enum
{
METHOD_NONE,
METHOD_PRIMITIVE,
METHOD_BLOCK
} MethodType;
typedef struct
{
MethodType type;
union
{
Primitive primitive;
ObjBlock* block;
};
} Method;
typedef struct
{
Obj obj;
// TODO(bob): Hack. Probably don't want to use this much space.
Method methods[MAX_SYMBOLS];
} ObjClass;
typedef enum
{
CODE_CONSTANT,
// Load the constant at index [arg].
CODE_CLASS,
// Define a new empty class and push it.
CODE_DUP,
// Push a copy of the top of stack.
CODE_POP,
// Pop and discard the top of stack.
@ -40,7 +85,7 @@ typedef enum
// Pushes the value in local slot [arg].
CODE_STORE_LOCAL,
// Pops and stores the value in local slot [arg].
// Stores the top of stack in local slot [arg]. Does not pop it.
CODE_CALL,
// Invoke the method with symbol [arg].
@ -50,18 +95,6 @@ typedef enum
} Code;
typedef struct
{
unsigned char* bytecode;
Value* constants;
int numConstants;
int numLocals;
} Block;
#define MAX_SYMBOLS 256
typedef Value (*Primitive)(Value receiver);
typedef struct
{
// TODO(bob): Make this dynamically sized.
@ -69,22 +102,18 @@ typedef struct
int count;
} SymbolTable;
typedef struct
{
// TODO(bob): Hack. Probably don't want to use this much space.
Primitive methods[MAX_SYMBOLS];
} Class;
typedef struct
{
SymbolTable symbols;
Class numClass;
ObjClass* numClass;
} VM;
VM* newVM();
void freeVM(VM* vm);
Value makeNum(int number);
ObjClass* makeClass();
ObjBlock* makeBlock();
ObjNum* makeNum(double number);
// Initializes the symbol table.
void initSymbolTable(SymbolTable* symbols);
@ -104,7 +133,7 @@ int ensureSymbol(SymbolTable* symbols, const char* name, size_t length);
// Looks up name in the symbol table. Returns its index if found or -1 if not.
int findSymbol(SymbolTable* symbols, const char* name, size_t length);
Value interpret(VM* vm, Block* block);
Value interpret(VM* vm, ObjBlock* block);
void printValue(Value value);