forked from Mirror/wren
Method calls.
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
var a = {
|
class Foo {
|
||||||
var b = 234
|
bar {
|
||||||
3
|
123
|
||||||
b
|
}
|
||||||
}
|
}
|
||||||
a.call
|
var a = Foo.new
|
||||||
|
a.bar
|
||||||
|
|||||||
@ -97,6 +97,7 @@ typedef struct sCompiler
|
|||||||
|
|
||||||
static ObjBlock* compileBlock(Parser* parser, Compiler* parent,
|
static ObjBlock* compileBlock(Parser* parser, Compiler* parent,
|
||||||
TokenType endToken);
|
TokenType endToken);
|
||||||
|
static int addConstant(Compiler* compiler, Value constant);
|
||||||
|
|
||||||
// Grammar:
|
// Grammar:
|
||||||
static void statement(Compiler* compiler);
|
static void statement(Compiler* compiler);
|
||||||
@ -185,6 +186,12 @@ ObjBlock* compileBlock(Parser* parser, Compiler* parent, TokenType endToken)
|
|||||||
return parser->hasError ? NULL : compiler.block;
|
return parser->hasError ? NULL : compiler.block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int addConstant(Compiler* compiler, Value constant)
|
||||||
|
{
|
||||||
|
compiler->block->constants[compiler->block->numConstants++] = constant;
|
||||||
|
return compiler->block->numConstants - 1;
|
||||||
|
}
|
||||||
|
|
||||||
void statement(Compiler* compiler)
|
void statement(Compiler* compiler)
|
||||||
{
|
{
|
||||||
if (match(compiler, TOKEN_CLASS))
|
if (match(compiler, TOKEN_CLASS))
|
||||||
@ -215,13 +222,20 @@ void statement(Compiler* compiler)
|
|||||||
{
|
{
|
||||||
// Method name.
|
// Method name.
|
||||||
consume(compiler, TOKEN_NAME);
|
consume(compiler, TOKEN_NAME);
|
||||||
//int symbol = internSymbol(compiler);
|
int symbol = internSymbol(compiler);
|
||||||
|
|
||||||
consume(compiler, TOKEN_LEFT_BRACE);
|
consume(compiler, TOKEN_LEFT_BRACE);
|
||||||
// TODO(bob): Parse body.
|
ObjBlock* method = compileBlock(compiler->parser, compiler,
|
||||||
consume(compiler, TOKEN_RIGHT_BRACE);
|
TOKEN_RIGHT_BRACE);
|
||||||
|
|
||||||
consume(compiler, TOKEN_LINE);
|
consume(compiler, TOKEN_LINE);
|
||||||
|
|
||||||
|
// Add the block to the constant table.
|
||||||
|
int constant = addConstant(compiler, (Value)method);
|
||||||
|
|
||||||
|
// Compile the code to define the method it.
|
||||||
|
emit(compiler, CODE_METHOD);
|
||||||
|
emit(compiler, symbol);
|
||||||
|
emit(compiler, constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -335,14 +349,11 @@ void number(Compiler* compiler)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Define a constant for the literal.
|
// Define a constant for the literal.
|
||||||
// TODO(bob): See if constant with same value already exists.
|
int constant = addConstant(compiler, (Value)makeNum((double)value));
|
||||||
Value constant = (Value)makeNum((double)value);
|
|
||||||
|
|
||||||
compiler->block->constants[compiler->block->numConstants++] = constant;
|
|
||||||
|
|
||||||
// Compile the code to load the constant.
|
// Compile the code to load the constant.
|
||||||
emit(compiler, CODE_CONSTANT);
|
emit(compiler, CODE_CONSTANT);
|
||||||
emit(compiler, compiler->block->numConstants - 1);
|
emit(compiler, constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenType peek(Compiler* compiler)
|
TokenType peek(Compiler* compiler)
|
||||||
|
|||||||
101
src/vm.c
101
src/vm.c
@ -36,7 +36,8 @@ typedef struct
|
|||||||
static void callBlock(Fiber* fiber, ObjBlock* block, int firstLocal);
|
static void callBlock(Fiber* fiber, ObjBlock* block, int firstLocal);
|
||||||
static void push(Fiber* fiber, Value value);
|
static void push(Fiber* fiber, Value value);
|
||||||
static Value pop(Fiber* fiber);
|
static Value pop(Fiber* fiber);
|
||||||
static Value primitive_num_abs(Value block);
|
static Value primitive_metaclass_new(Value receiver);
|
||||||
|
static Value primitive_num_abs(Value receiver);
|
||||||
|
|
||||||
VM* newVM()
|
VM* newVM()
|
||||||
{
|
{
|
||||||
@ -66,7 +67,7 @@ void freeVM(VM* vm)
|
|||||||
free(vm);
|
free(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjClass* makeClass()
|
ObjClass* makeSingleClass()
|
||||||
{
|
{
|
||||||
ObjClass* obj = malloc(sizeof(ObjClass));
|
ObjClass* obj = malloc(sizeof(ObjClass));
|
||||||
obj->obj.type = OBJ_CLASS;
|
obj->obj.type = OBJ_CLASS;
|
||||||
@ -80,6 +81,17 @@ ObjClass* makeClass()
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjClass* makeClass()
|
||||||
|
{
|
||||||
|
ObjClass* classObj = makeSingleClass();
|
||||||
|
|
||||||
|
// Make its metaclass.
|
||||||
|
// TODO(bob): What is the metaclass's metaclass?
|
||||||
|
classObj->metaclass = makeSingleClass();
|
||||||
|
|
||||||
|
return classObj;
|
||||||
|
}
|
||||||
|
|
||||||
ObjBlock* makeBlock()
|
ObjBlock* makeBlock()
|
||||||
{
|
{
|
||||||
ObjBlock* block = malloc(sizeof(ObjBlock));
|
ObjBlock* block = malloc(sizeof(ObjBlock));
|
||||||
@ -97,6 +109,16 @@ ObjNum* makeNum(double number)
|
|||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjInstance* makeInstance(ObjClass* classObj)
|
||||||
|
{
|
||||||
|
ObjInstance* instance = malloc(sizeof(ObjInstance));
|
||||||
|
instance->obj.type = OBJ_INSTANCE;
|
||||||
|
instance->obj.flags = 0;
|
||||||
|
instance->classObj = classObj;
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
void initSymbolTable(SymbolTable* symbols)
|
void initSymbolTable(SymbolTable* symbols)
|
||||||
{
|
{
|
||||||
symbols->count = 0;
|
symbols->count = 0;
|
||||||
@ -150,6 +172,11 @@ int findSymbol(SymbolTable* symbols, const char* name, size_t length)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* getSymbolName(SymbolTable* symbols, int symbol)
|
||||||
|
{
|
||||||
|
return symbols->names[symbol];
|
||||||
|
}
|
||||||
|
|
||||||
Value interpret(VM* vm, ObjBlock* block)
|
Value interpret(VM* vm, ObjBlock* block)
|
||||||
{
|
{
|
||||||
Fiber fiber;
|
Fiber fiber;
|
||||||
@ -176,9 +203,37 @@ Value interpret(VM* vm, ObjBlock* block)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case CODE_CLASS:
|
case CODE_CLASS:
|
||||||
push(&fiber, (Value)makeClass());
|
{
|
||||||
|
ObjClass* classObj = makeClass();
|
||||||
|
|
||||||
|
// Define a "new" method on the metaclass.
|
||||||
|
// TODO(bob): Can this be inherited?
|
||||||
|
int newSymbol = ensureSymbol(&vm->symbols, "new", strlen("new"));
|
||||||
|
printf("define new %d\n", newSymbol);
|
||||||
|
classObj->metaclass->methods[newSymbol].type = METHOD_PRIMITIVE;
|
||||||
|
classObj->metaclass->methods[newSymbol].primitive =
|
||||||
|
primitive_metaclass_new;
|
||||||
|
|
||||||
|
push(&fiber, (Value)classObj);
|
||||||
printf("push class at %d\n", fiber.stackSize - 1);
|
printf("push class at %d\n", fiber.stackSize - 1);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CODE_METHOD:
|
||||||
|
{
|
||||||
|
int symbol = frame->block->bytecode[frame->ip++];
|
||||||
|
int constant = frame->block->bytecode[frame->ip++];
|
||||||
|
ObjClass* classObj = (ObjClass*)fiber.stack[fiber.stackSize - 1];
|
||||||
|
|
||||||
|
ObjBlock* body = (ObjBlock*)frame->block->constants[constant];
|
||||||
|
classObj->methods[symbol].type = METHOD_BLOCK;
|
||||||
|
classObj->methods[symbol].block = body;
|
||||||
|
|
||||||
|
printf("define method %d using constant %d on ", symbol, constant);
|
||||||
|
printValue((Value)classObj);
|
||||||
|
printf("\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case CODE_LOAD_LOCAL:
|
case CODE_LOAD_LOCAL:
|
||||||
{
|
{
|
||||||
@ -222,14 +277,22 @@ Value interpret(VM* vm, ObjBlock* block)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case OBJ_CLASS:
|
case OBJ_CLASS:
|
||||||
classObj = vm->classClass;
|
classObj = ((ObjClass*)receiver)->metaclass;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OBJ_NUM:
|
case OBJ_NUM:
|
||||||
classObj = vm->numClass;
|
classObj = vm->numClass;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OBJ_INSTANCE:
|
||||||
|
classObj = ((ObjInstance*)receiver)->classObj;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("call %d on ", symbol);
|
||||||
|
printValue(receiver);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
Method* method = &classObj->methods[symbol];
|
Method* method = &classObj->methods[symbol];
|
||||||
switch (method->type)
|
switch (method->type)
|
||||||
{
|
{
|
||||||
@ -262,11 +325,23 @@ Value interpret(VM* vm, ObjBlock* block)
|
|||||||
fiber.numFrames--;
|
fiber.numFrames--;
|
||||||
|
|
||||||
// If we are returning from the top-level block, just return the value.
|
// If we are returning from the top-level block, just return the value.
|
||||||
if (fiber.numFrames == 0) return result;
|
if (fiber.numFrames == 0)
|
||||||
|
{
|
||||||
|
printf("done with result ");
|
||||||
|
printValue(result);
|
||||||
|
printf("\n");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Store the result of the block in the first slot, which is where the
|
// Store the result of the block in the first slot, which is where the
|
||||||
// caller expects it.
|
// caller expects it.
|
||||||
|
printf("return and store result ");
|
||||||
|
printValue(result);
|
||||||
|
printf(" in %d\n", frame->locals);
|
||||||
fiber.stack[frame->locals] = result;
|
fiber.stack[frame->locals] = result;
|
||||||
|
|
||||||
|
// Discard the stack slots for the locals.
|
||||||
|
fiber.stackSize = frame->locals + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,6 +349,7 @@ Value interpret(VM* vm, ObjBlock* block)
|
|||||||
|
|
||||||
void printValue(Value value)
|
void printValue(Value value)
|
||||||
{
|
{
|
||||||
|
// TODO(bob): Do more useful stuff here.
|
||||||
switch (value->type)
|
switch (value->type)
|
||||||
{
|
{
|
||||||
case OBJ_NUM:
|
case OBJ_NUM:
|
||||||
@ -287,6 +363,10 @@ void printValue(Value value)
|
|||||||
case OBJ_CLASS:
|
case OBJ_CLASS:
|
||||||
printf("[class]");
|
printf("[class]");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OBJ_INSTANCE:
|
||||||
|
printf("[instance]");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,9 +398,16 @@ Value pop(Fiber* fiber)
|
|||||||
return fiber->stack[--fiber->stackSize];
|
return fiber->stack[--fiber->stackSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
Value primitive_num_abs(Value number)
|
Value primitive_metaclass_new(Value receiver)
|
||||||
{
|
{
|
||||||
double value = ((ObjNum*)number)->value;
|
ObjClass* classObj = (ObjClass*)receiver;
|
||||||
|
// TODO(bob): Invoke initializer method.
|
||||||
|
return (Value)makeInstance(classObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value primitive_num_abs(Value receiver)
|
||||||
|
{
|
||||||
|
double value = ((ObjNum*)receiver)->value;
|
||||||
if (value < 0) value = -value;
|
if (value < 0) value = -value;
|
||||||
|
|
||||||
return (Value)makeNum(value);
|
return (Value)makeNum(value);
|
||||||
|
|||||||
21
src/vm.h
21
src/vm.h
@ -9,7 +9,8 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
OBJ_NUM,
|
OBJ_NUM,
|
||||||
OBJ_BLOCK,
|
OBJ_BLOCK,
|
||||||
OBJ_CLASS
|
OBJ_CLASS,
|
||||||
|
OBJ_INSTANCE
|
||||||
} ObjType;
|
} ObjType;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
@ -61,13 +62,21 @@ typedef struct
|
|||||||
};
|
};
|
||||||
} Method;
|
} Method;
|
||||||
|
|
||||||
typedef struct
|
typedef struct sObjClass
|
||||||
{
|
{
|
||||||
Obj obj;
|
Obj obj;
|
||||||
|
struct sObjClass* metaclass;
|
||||||
// TODO(bob): Hack. Probably don't want to use this much space.
|
// TODO(bob): Hack. Probably don't want to use this much space.
|
||||||
Method methods[MAX_SYMBOLS];
|
Method methods[MAX_SYMBOLS];
|
||||||
} ObjClass;
|
} ObjClass;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Obj obj;
|
||||||
|
ObjClass* classObj;
|
||||||
|
// TODO(bob): Fields.
|
||||||
|
} ObjInstance;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
CODE_CONSTANT,
|
CODE_CONSTANT,
|
||||||
@ -76,6 +85,10 @@ typedef enum
|
|||||||
CODE_CLASS,
|
CODE_CLASS,
|
||||||
// Define a new empty class and push it.
|
// Define a new empty class and push it.
|
||||||
|
|
||||||
|
CODE_METHOD,
|
||||||
|
// Add a method for symbol [arg1] with body stored in constant [arg2] to the
|
||||||
|
// class on the top of stack. Does not modify the stack.
|
||||||
|
|
||||||
CODE_DUP,
|
CODE_DUP,
|
||||||
// Push a copy of the top of stack.
|
// Push a copy of the top of stack.
|
||||||
|
|
||||||
@ -118,6 +131,7 @@ void freeVM(VM* vm);
|
|||||||
ObjClass* makeClass();
|
ObjClass* makeClass();
|
||||||
ObjBlock* makeBlock();
|
ObjBlock* makeBlock();
|
||||||
ObjNum* makeNum(double number);
|
ObjNum* makeNum(double number);
|
||||||
|
ObjInstance* makeInstance(ObjClass* classObj);
|
||||||
|
|
||||||
// Initializes the symbol table.
|
// Initializes the symbol table.
|
||||||
void initSymbolTable(SymbolTable* symbols);
|
void initSymbolTable(SymbolTable* symbols);
|
||||||
@ -137,6 +151,9 @@ 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.
|
// 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);
|
int findSymbol(SymbolTable* symbols, const char* name, size_t length);
|
||||||
|
|
||||||
|
// Given an index in the symbol table, returns its name.
|
||||||
|
const char* getSymbolName(SymbolTable* symbols, int symbol);
|
||||||
|
|
||||||
Value interpret(VM* vm, ObjBlock* block);
|
Value interpret(VM* vm, ObjBlock* block);
|
||||||
|
|
||||||
void printValue(Value value);
|
void printValue(Value value);
|
||||||
|
|||||||
Reference in New Issue
Block a user