mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-10 21:58:48 +01:00
Method calls.
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
var a = {
|
||||
var b = 234
|
||||
3
|
||||
b
|
||||
class Foo {
|
||||
bar {
|
||||
123
|
||||
}
|
||||
}
|
||||
a.call
|
||||
var a = Foo.new
|
||||
a.bar
|
||||
|
||||
@ -97,6 +97,7 @@ typedef struct sCompiler
|
||||
|
||||
static ObjBlock* compileBlock(Parser* parser, Compiler* parent,
|
||||
TokenType endToken);
|
||||
static int addConstant(Compiler* compiler, Value constant);
|
||||
|
||||
// Grammar:
|
||||
static void statement(Compiler* compiler);
|
||||
@ -185,6 +186,12 @@ ObjBlock* compileBlock(Parser* parser, Compiler* parent, TokenType endToken)
|
||||
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)
|
||||
{
|
||||
if (match(compiler, TOKEN_CLASS))
|
||||
@ -215,13 +222,20 @@ void statement(Compiler* compiler)
|
||||
{
|
||||
// Method name.
|
||||
consume(compiler, TOKEN_NAME);
|
||||
//int symbol = internSymbol(compiler);
|
||||
int symbol = internSymbol(compiler);
|
||||
|
||||
consume(compiler, TOKEN_LEFT_BRACE);
|
||||
// TODO(bob): Parse body.
|
||||
consume(compiler, TOKEN_RIGHT_BRACE);
|
||||
|
||||
ObjBlock* method = compileBlock(compiler->parser, compiler,
|
||||
TOKEN_RIGHT_BRACE);
|
||||
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;
|
||||
@ -335,14 +349,11 @@ void number(Compiler* compiler)
|
||||
}
|
||||
|
||||
// Define a constant for the literal.
|
||||
// TODO(bob): See if constant with same value already exists.
|
||||
Value constant = (Value)makeNum((double)value);
|
||||
|
||||
compiler->block->constants[compiler->block->numConstants++] = constant;
|
||||
int constant = addConstant(compiler, (Value)makeNum((double)value));
|
||||
|
||||
// Compile the code to load the constant.
|
||||
emit(compiler, CODE_CONSTANT);
|
||||
emit(compiler, compiler->block->numConstants - 1);
|
||||
emit(compiler, constant);
|
||||
}
|
||||
|
||||
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 push(Fiber* fiber, Value value);
|
||||
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()
|
||||
{
|
||||
@ -66,7 +67,7 @@ void freeVM(VM* vm)
|
||||
free(vm);
|
||||
}
|
||||
|
||||
ObjClass* makeClass()
|
||||
ObjClass* makeSingleClass()
|
||||
{
|
||||
ObjClass* obj = malloc(sizeof(ObjClass));
|
||||
obj->obj.type = OBJ_CLASS;
|
||||
@ -80,6 +81,17 @@ ObjClass* makeClass()
|
||||
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* block = malloc(sizeof(ObjBlock));
|
||||
@ -97,6 +109,16 @@ ObjNum* makeNum(double number)
|
||||
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)
|
||||
{
|
||||
symbols->count = 0;
|
||||
@ -150,6 +172,11 @@ int findSymbol(SymbolTable* symbols, const char* name, size_t length)
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* getSymbolName(SymbolTable* symbols, int symbol)
|
||||
{
|
||||
return symbols->names[symbol];
|
||||
}
|
||||
|
||||
Value interpret(VM* vm, ObjBlock* block)
|
||||
{
|
||||
Fiber fiber;
|
||||
@ -176,9 +203,37 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
}
|
||||
|
||||
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);
|
||||
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:
|
||||
{
|
||||
@ -222,14 +277,22 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
break;
|
||||
|
||||
case OBJ_CLASS:
|
||||
classObj = vm->classClass;
|
||||
classObj = ((ObjClass*)receiver)->metaclass;
|
||||
break;
|
||||
|
||||
case OBJ_NUM:
|
||||
classObj = vm->numClass;
|
||||
break;
|
||||
|
||||
case OBJ_INSTANCE:
|
||||
classObj = ((ObjInstance*)receiver)->classObj;
|
||||
break;
|
||||
}
|
||||
|
||||
printf("call %d on ", symbol);
|
||||
printValue(receiver);
|
||||
printf("\n");
|
||||
|
||||
Method* method = &classObj->methods[symbol];
|
||||
switch (method->type)
|
||||
{
|
||||
@ -262,11 +325,23 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
fiber.numFrames--;
|
||||
|
||||
// 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
|
||||
// caller expects it.
|
||||
printf("return and store result ");
|
||||
printValue(result);
|
||||
printf(" in %d\n", frame->locals);
|
||||
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)
|
||||
{
|
||||
// TODO(bob): Do more useful stuff here.
|
||||
switch (value->type)
|
||||
{
|
||||
case OBJ_NUM:
|
||||
@ -287,6 +363,10 @@ void printValue(Value value)
|
||||
case OBJ_CLASS:
|
||||
printf("[class]");
|
||||
break;
|
||||
|
||||
case OBJ_INSTANCE:
|
||||
printf("[instance]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,9 +398,16 @@ Value pop(Fiber* fiber)
|
||||
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;
|
||||
|
||||
return (Value)makeNum(value);
|
||||
|
||||
21
src/vm.h
21
src/vm.h
@ -9,7 +9,8 @@
|
||||
typedef enum {
|
||||
OBJ_NUM,
|
||||
OBJ_BLOCK,
|
||||
OBJ_CLASS
|
||||
OBJ_CLASS,
|
||||
OBJ_INSTANCE
|
||||
} ObjType;
|
||||
|
||||
typedef enum
|
||||
@ -61,13 +62,21 @@ typedef struct
|
||||
};
|
||||
} Method;
|
||||
|
||||
typedef struct
|
||||
typedef struct sObjClass
|
||||
{
|
||||
Obj obj;
|
||||
struct sObjClass* metaclass;
|
||||
// TODO(bob): Hack. Probably don't want to use this much space.
|
||||
Method methods[MAX_SYMBOLS];
|
||||
} ObjClass;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Obj obj;
|
||||
ObjClass* classObj;
|
||||
// TODO(bob): Fields.
|
||||
} ObjInstance;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CODE_CONSTANT,
|
||||
@ -76,6 +85,10 @@ typedef enum
|
||||
CODE_CLASS,
|
||||
// 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,
|
||||
// Push a copy of the top of stack.
|
||||
|
||||
@ -118,6 +131,7 @@ void freeVM(VM* vm);
|
||||
ObjClass* makeClass();
|
||||
ObjBlock* makeBlock();
|
||||
ObjNum* makeNum(double number);
|
||||
ObjInstance* makeInstance(ObjClass* classObj);
|
||||
|
||||
// Initializes the symbol table.
|
||||
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.
|
||||
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);
|
||||
|
||||
void printValue(Value value);
|
||||
|
||||
Reference in New Issue
Block a user