فهرست منبع

method and initializer

runningwater 2 سال پیش
والد
کامیت
09dbc6dcda
9فایلهای تغییر یافته به همراه110 افزوده شده و 9 حذف شده
  1. 1 0
      chunk.h
  2. 1 1
      common.h
  3. 25 2
      compiler.c
  4. 9 0
      debug.c
  5. 2 1
      memory.c
  6. 12 0
      test/init.lox
  7. 2 1
      test/this.lox
  8. 53 1
      vm.c
  9. 5 3
      vm.h

+ 1 - 0
chunk.h

@@ -47,6 +47,7 @@ typedef enum {
   OP_CLOSE_UPVALUE,
   OP_SET_PROPERTY,
   OP_GET_PROPERTY,
+  OP_INVOKE,
   OP_CLASS,
   OP_METHOD,
 } OpCode;

+ 1 - 1
common.h

@@ -19,7 +19,7 @@
 //#define DEBUG_STRESS_GC
 #define DEBUG_LOG_GC
 
-#define PRINT_METADATA
+//#define PRINT_METADATA
 
 #define UINT8_COUNT (UINT8_MAX + 1)
 

+ 25 - 2
compiler.c

@@ -56,10 +56,11 @@ typedef struct {
 typedef enum {
   TYPE_METHOD,
   TYPE_FUNCTION,
+  TYPE_INITIALIZER,
   TYPE_SCRIPT,
 } FunctionType;
 
-typedef struct {
+typedef struct Compiler {
   struct Compiler *enclosing;
   ObjFunction *function;
   FunctionType type;
@@ -173,7 +174,11 @@ static int emitJump(uint8_t instruction) {
   return currentChunk()->count - 2;
 }
 static void emitReturn() {
-  emitByte(OP_NIL);
+  if (current->type == TYPE_INITIALIZER) {
+    emitBytes(OP_GET_LOCAL, 0);
+  } else {
+    emitByte(OP_NIL);
+  }
   emitByte(OP_RETURN);
 }
 static uint8_t makeConstant(Value value) {
@@ -217,6 +222,12 @@ static void function(FunctionType type) {
     emitByte(compiler.upvalues[i].isLocal ? 1 : 0);
     emitByte(compiler.upvalues[i].index);
   }
+
+  //! NOTE:
+  //!     this beginScope() doesn't have a corresponding endScope() call.
+  //!     Because we end Compiler completely when we reach the end of the
+  //!     function body, there's no need to close the lingering outermost
+  //!     scope.
 }
 static void method() {
   //!  To define a new method, the VM needs three things:
@@ -231,6 +242,9 @@ static void method() {
 
   // Method Body ObjClosure <OP_CLOSURE>
   FunctionType type = TYPE_METHOD;
+  if (parser.previous.length == 4 && memcmp(parser.previous.start, "init", 4) == 0) {
+    type = TYPE_INITIALIZER;
+  }
   function(type);
 
   emitBytes(OP_METHOD, constant);
@@ -366,6 +380,10 @@ static void returnStatement() {
   if (match(TOKEN_SEMICOLON)) {
     emitReturn();
   } else {
+    if (current->type == TYPE_INITIALIZER) {
+      error("Can't return a value from an initializer.");
+    }
+
     expression();
     consume(TOKEN_SEMICOLON, "Expect ';' after return value.");
     emitByte(OP_RETURN);
@@ -763,6 +781,11 @@ static void dot(bool canAssign) {
   if (canAssign && match(TOKEN_EQUAL)) {
     expression();
     emitBytes(OP_SET_PROPERTY, name);
+  } else if (match(TOKEN_LEFT_PAREN)) {
+    // Instance.method()
+    uint8_t argCount = argumentList();
+    emitBytes(OP_INVOKE, name);
+    emitByte(argCount);
   } else {
     emitBytes(OP_GET_PROPERTY, name);
   }

+ 9 - 0
debug.c

@@ -24,6 +24,14 @@ static int jumpInstruction(const char *name, int sign, Chunk *chunk, int offset)
   printf("%-16s %4d -> %d\n", name, offset, offset + 3 + sign * jump);
   return offset + 3;
 }
+static int invokeInstruction(const char *name, Chunk *chunk, int offset) {
+  uint8_t constant = chunk->code[offset + 1];
+  uint8_t argCount = chunk->code[offset + 2];
+  printf("%-16s (%d args) %4d '", name, argCount, constant);
+  printValue(chunk->constants.values[constant]);
+  printf("\n");
+  return offset + 3;
+}
 int disassembleInstruction(Chunk *chunk, int offset) {
   printf("%04d ", offset);
 
@@ -82,6 +90,7 @@ int disassembleInstruction(Chunk *chunk, int offset) {
     case OP_METHOD: return constantInstruction("OP_METHOD", chunk, offset);
     case OP_GET_PROPERTY: return constantInstruction("OP_GET_PROPERTY", chunk, offset);
     case OP_SET_PROPERTY: return constantInstruction("OP_SET_PROPERTY", chunk, offset);
+    case OP_INVOKE: return invokeInstruction("OP_INVOKE", chunk, offset);
     case OP_RETURN: return simpleInstruction("OP_RETURN", offset);
     default:
       printf("Unknown opcode %d\n", instruction);

+ 2 - 1
memory.c

@@ -50,7 +50,7 @@ void *reallocate(void *pointer, size_t oldSize, size_t newSize) {
 }
 static void freeObject(Obj *object) {
 #ifdef DEBUG_LOG_GC
-  PRINTLNF("%p free type %d\n", (void *) object, object->type);
+  PRINTLNF("%p free type %d", (void *) object, object->type);
 #endif
 
   switch (object->type) {
@@ -119,6 +119,7 @@ static void markRoots() {
 
   markTable(&vm.globals);
   markCompilerRoots();
+  markObject((Obj *) vm.initString);
 }
 void blackenObject(Obj *object) {
 #ifdef DEBUG_LOG_GC

+ 12 - 0
test/init.lox

@@ -0,0 +1,12 @@
+class Oops {
+  init() {
+    fun f() {
+      print "not a method";
+    }
+
+    this.field = f;
+  }
+}
+
+var oops = Oops();
+oops.field();

+ 2 - 1
test/this.lox

@@ -9,4 +9,5 @@ class Nested {
   }
 }
 
-Nested().method();
+Nested().method();
+

+ 53 - 1
vm.c

@@ -71,6 +71,10 @@ void initVM() {
   initTable(&vm.globals);
   initTable(&vm.strings);
 
+  // 类初始化方法
+  vm.initString = NULL;
+  vm.initString = copyString("init", 4);
+
   /// Native Function define.
   defineNative("clock", clockNative);
 }
@@ -100,7 +104,7 @@ static bool callValue(Value callee, int argCount) {
     switch (OBJ_TYPE(callee)) {
       case OBJ_BOUND_METHOD: {
         ObjBoundMethod *bound = AS_BOUND_METHOD(callee);
-        vm.stackTop[-argCount-1] = bound->receiver;
+        vm.stackTop[-argCount - 1] = bound->receiver;
         return call(bound->method, argCount);
       }
       case OBJ_CLOSURE:
@@ -114,7 +118,21 @@ static bool callValue(Value callee, int argCount) {
       }
       case OBJ_CLASS: {
         ObjClass *klass = AS_CLASS(callee);
+        // stack 中 class 替换成 instance
         vm.stackTop[-argCount - 1] = OBJ_VAL(newInstance(klass));
+        // 初始化方法
+        // class Brunch {
+        //  init(food, drink) {}
+        //}
+        //
+        //Brunch("eggs", "coffee");
+        Value initializer;
+        if (tableGet(&klass->methods, vm.initString, &initializer)) {
+          return call(AS_CLOSURE(initializer), argCount);
+        } else if (argCount != 0) {
+          runtimeError("Expected 0 arguments but got %d.", argCount);
+          return false;
+        }
         return true;
       }
       default:
@@ -190,6 +208,30 @@ static bool bindKlassMethod(ObjClass *klass, ObjString *name) {
   push(OBJ_VAL(bound));
   return true;
 }
+static bool invokeFromClas(ObjClass *klass, ObjString *name, int argCount) {
+  Value method;
+  if (!tableGet(&klass->methods, name, &method)) {
+    runtimeError("Undefined property '%s'.", name->chars);
+    return false;
+  }
+  return call(AS_CLOSURE(method), argCount);
+}
+static bool invoke(ObjString *name, int argCount) {
+  Value receiver = peek(argCount);
+  if (!IS_INSTANCE(receiver)) {
+    runtimeError("Only instances have methods.");
+    return false;
+  }
+  ObjInstance *instance = AS_INSTANCE(receiver);
+
+  Value value;
+  if (tableGet(&instance->fields, name, &value)) {
+    vm.stackTop[-argCount - 1] = value;
+    return callValue(value, argCount);
+  }
+
+  return invokeFromClas(instance->klass, name, argCount);
+}
 /// VM run function - exec opcode
 /// \return
 static InterpretResult run() {
@@ -407,6 +449,15 @@ static InterpretResult run() {
         push(value);
         break;
       }
+      case OP_INVOKE: {
+        ObjString *method = READ_STRING();
+        int argCount = READ_BYTE();
+        if (!invoke(method, argCount)) {
+          return INTERPRET_RUNTIME_ERROR;
+        }
+        frame = &vm.frames[vm.frameCount - 1];
+        break;
+      }
       case OP_CLOSURE: {
         ObjFunction *function = AS_FUNCTION(READ_CONSTANT());
         ObjClosure *closure = newClosure(function);
@@ -465,6 +516,7 @@ static InterpretResult run() {
 void freeVM() {
   freeTable(&vm.globals);
   freeTable(&vm.strings);
+  vm.initString = NULL;
   freeObjects();
 }
 

+ 5 - 3
vm.h

@@ -21,7 +21,8 @@
 typedef struct {
   ObjClosure *closure;
   uint8_t *ip;
-  Value *slots;
+  Value *slots;//  points into the VM’s value stack
+  // at the first slot that this function can use
 } CallFrame;
 
 typedef struct VM {
@@ -31,7 +32,8 @@ typedef struct VM {
   Value stack[STACK_MAX];
   Value *stackTop;// 栈指针
   Table globals;
-  Table strings;//
+  Table strings;        //
+  ObjString *initString;// 初始化方法
   ObjUpvalue *openUpvalues;
 
   size_t bytesAllocated;
@@ -39,7 +41,7 @@ typedef struct VM {
   Obj *objects;// 管理分配的 heap 内存
   int grayCount;
   int grayCapacity;
-  Obj** grayStack;
+  Obj **grayStack;
 } VM;
 typedef enum {
   INTERPRET_OK,