Преглед изворни кода

method declare and reference

runningwater пре 2 година
родитељ
комит
10f1ee0d4c
11 измењених фајлова са 200 додато и 13 уклоњено
  1. 1 0
      chunk.h
  2. 49 0
      common.h
  3. 52 4
      compiler.c
  4. 1 0
      debug.c
  5. 13 1
      memory.c
  6. 12 2
      object.c
  7. 11 0
      object.h
  8. 4 0
      test/local.lox
  9. 8 0
      test/method.lox
  10. 12 0
      test/this.lox
  11. 37 6
      vm.c

+ 1 - 0
chunk.h

@@ -48,6 +48,7 @@ typedef enum {
   OP_SET_PROPERTY,
   OP_GET_PROPERTY,
   OP_CLASS,
+  OP_METHOD,
 } OpCode;
 
 //============================================================================

+ 49 - 0
common.h

@@ -19,6 +19,55 @@
 //#define DEBUG_STRESS_GC
 #define DEBUG_LOG_GC
 
+#define PRINT_METADATA
+
 #define UINT8_COUNT (UINT8_MAX + 1)
 
+
+#ifdef PRINT_METADATA
+#define PRINTLNF(format, ...) printf("("__FILE__              \
+                                     ":%d) %s: " format "\n", \
+                                     __LINE__, __FUNCTION__, ##__VA_ARGS__)
+#else
+#define PRINTLNF(format, ...) printf(format "\n", ##__VA_ARGS__)
+#endif
+
+#define PRINT_CHAR(char_value) PRINTLNF(#char_value ": %c", char_value)
+#define PRINT_WCHAR(char_value) PRINTLNF(#char_value ": %lc", char_value)
+#define PRINT_INT(int_value) PRINTLNF(#int_value " : %d", int_value)
+#define PRINT_LONG(long_value) PRINTLNF(#long_value ": %ld", long_value)
+#define PRINT_LLONG(long_value) PRINTLNF(#long_value ": %lld", long_value)
+#define PRINT_BINARY(int_value) PrintBinary((unsigned int) int_value);
+#define PRINT_HEX(int_value) PRINTLNF(#int_value ": %#x", int_value)
+#define PRINT_BOOL(bool_value) PRINTLNF(#bool_value ": %s", bool_value ? "true" : "false")
+#define PRINT_DOUBLE(double_value) PRINTLNF(#double_value ": %g", double_value)
+#define PRINT_STRING(string_value) PRINTLNF(#string_value ": %s", string_value)
+
+#define PRINT_ARRAY(format, array, length)                       \
+  {                                                              \
+    int array_index;                                             \
+    for (array_index = 0; array_index < length; ++array_index) { \
+      printf(format, array[array_index]);                        \
+    };                                                           \
+    printf("\n");                                                \
+  }
+
+#define PRINT_INT_ARRAY_LN(array, length)       \
+  {                                             \
+    int i;                                      \
+    for (i = 0; i < length; ++i) {              \
+      PRINTLNF(#array "[%d]: %d", i, array[i]); \
+    }                                           \
+  }
+
+#define PRINT_INT_ARRAY(array, length) PRINT_ARRAY("%d, ", array, length)
+#define PRINT_CHAR_ARRAY(array, length) PRINT_ARRAY("%c, ", array, length)
+#define PRINT_DOUBLE_ARRAY(array, length) PRINT_ARRAY("%g, ", array, length)
+
+#define PRINT_IF_ERROR(format, ...)             \
+  if (errno != 0) {                             \
+    fprintf(stderr, format, ##__VA_ARGS__);     \
+    fprintf(stderr, ": %s\n", strerror(errno)); \
+  }
+
 #endif//CLOX__COMMON_H_

+ 52 - 4
compiler.c

@@ -54,11 +54,12 @@ typedef struct {
   bool isLocal;
 } Upvalue;
 typedef enum {
+  TYPE_METHOD,
   TYPE_FUNCTION,
   TYPE_SCRIPT,
 } FunctionType;
 
-typedef struct Compiler {
+typedef struct {
   struct Compiler *enclosing;
   ObjFunction *function;
   FunctionType type;
@@ -69,8 +70,13 @@ typedef struct Compiler {
   int scopeDepth;
 } Compiler;
 
+typedef struct ClassCompiler {
+  struct ClassCompiler *enclosing;
+} ClassCompiler;
+
 Parser parser;
 Compiler *current = NULL;
+ClassCompiler *currentClass = NULL;
 
 static void parsePrecedence(Precedence);
 static uint8_t parseVariable(const char *);
@@ -82,6 +88,7 @@ static ParseRule *getRule(TokenType);
 static void statement();
 static void declaration();
 static void markInitialized();
+static void namedVariable(Token name, bool canAssign);
 
 static void initCompiler(Compiler *compiler, FunctionType type);
 
@@ -211,16 +218,45 @@ static void function(FunctionType type) {
     emitByte(compiler.upvalues[i].index);
   }
 }
+static void method() {
+  //!  To define a new method, the VM needs three things:
+  //!
+  //!    1. The name of the method.
+  //!
+  //!    2. The closure for the method body.
+  //!
+  //!    3. The class to bind the method to.
+  consume(TOKEN_IDENTIFIER, "Expect method name.");
+  uint8_t constant = identifierConstant(&parser.previous);
+
+  // Method Body ObjClosure <OP_CLOSURE>
+  FunctionType type = TYPE_METHOD;
+  function(type);
+
+  emitBytes(OP_METHOD, constant);
+}
 static void classDeclaration() {
   consume(TOKEN_IDENTIFIER, "Expect class name.");
+  Token className = parser.previous;
   uint8_t nameConstant = identifierConstant(&parser.previous);
   declareVariable();
 
   emitBytes(OP_CLASS, nameConstant);
   defineVariable(nameConstant);
 
+  ClassCompiler classCompiler;
+  classCompiler.enclosing = currentClass;
+  currentClass = &classCompiler;
+
+  namedVariable(className, false);
   consume(TOKEN_LEFT_BRACE, "Expect '{' before class body.");
+  while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
+    method();
+  }
   consume(TOKEN_RIGHT_BRACE, "Expect '}' after class body.");
+  emitByte(OP_POP);
+
+  currentClass = currentClass->enclosing;
 }
 static void funDeclaration() {
   uint8_t global = parseVariable("Expect function name.");
@@ -478,8 +514,13 @@ static void initCompiler(Compiler *compiler, FunctionType type) {
   Local *local = &current->locals[current->localCount++];
   local->depth = 0;
   local->isCaptured = false;
-  local->name.start = "";
-  local->name.length = 0;
+  if (type != TYPE_FUNCTION) {
+    local->name.start = "this";
+    local->name.length = 4;
+  } else {
+    local->name.start = "";
+    local->name.length = 0;
+  }
 }
 static ObjFunction *endCompiler() {
   emitReturn();
@@ -614,6 +655,13 @@ static void namedVariable(Token name, bool canAssign) {
 static void variable(bool canAssign) {
   namedVariable(parser.previous, canAssign);
 }
+static void this_(__attribute__((unused)) bool canAssign) {
+  if (currentClass == NULL) {
+    error("Can't use 'this' outside of a class.");
+    return;
+  }
+  variable(false);
+}
 /// Unary negation: -123
 static void unary(__attribute__((unused)) bool canAssign) {
   TokenType operatorType = parser.previous.type;
@@ -754,7 +802,7 @@ ParseRule rules[] = {
     [TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
     [TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
     [TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
-    [TOKEN_THIS] = {NULL, NULL, PREC_NONE},
+    [TOKEN_THIS] = {this_, NULL, PREC_NONE},
     [TOKEN_TRUE] = {literal, NULL, PREC_NONE},
     [TOKEN_VAR] = {NULL, NULL, PREC_NONE},
     [TOKEN_WHILE] = {NULL, NULL, PREC_NONE},

+ 1 - 0
debug.c

@@ -79,6 +79,7 @@ int disassembleInstruction(Chunk *chunk, int offset) {
     case OP_SET_UPVALUE: return byteInstruction("OP_SET_UPVALUE", chunk, offset);
     case OP_CLOSE_UPVALUE: return simpleInstruction("OP_CLOSE_UPVALUE", offset);
     case OP_CLASS: return constantInstruction("OP_CLASS", chunk, 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_RETURN: return simpleInstruction("OP_RETURN", offset);

+ 13 - 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
-  printf("%p free type %d\n", (void *) object, object->type);
+  PRINTLNF("%p free type %d\n", (void *) object, object->type);
 #endif
 
   switch (object->type) {
@@ -77,6 +77,8 @@ static void freeObject(Obj *object) {
       FREE(ObjUpvalue, object);
       break;
     case OBJ_CLASS: {
+      ObjClass *klass = (ObjClass *) object;
+      freeTable(&klass->methods);
       FREE(ObjClass, object);
       break;
     }
@@ -86,6 +88,9 @@ static void freeObject(Obj *object) {
       FREE(ObjInstance, object);
       break;
     }
+    case OBJ_BOUND_METHOD:
+      FREE(ObjBoundMethod, object);
+      break;
   }
 }
 void freeObjects() {
@@ -132,6 +137,7 @@ void blackenObject(Obj *object) {
     case OBJ_CLASS: {
       ObjClass *klass = (ObjClass *) object;
       markObject((Obj *) klass->name);
+      markTable(&klass->methods);
       break;
     }
     case OBJ_CLOSURE: {
@@ -154,6 +160,12 @@ void blackenObject(Obj *object) {
     case OBJ_NATIVE:
     case OBJ_STRING:
       break;
+    case OBJ_BOUND_METHOD: {
+      ObjBoundMethod *bound = (ObjBoundMethod *) object;
+      markValue(bound->receiver);
+      markObject((Obj *) bound->method);
+      break;
+    }
   }
 }
 void traceReferences() {

+ 12 - 2
object.c

@@ -72,11 +72,14 @@ void printObject(Value value) {
       printf("upvalue");
       break;
     case OBJ_CLASS:
-      printf("%s", AS_CLASS(value)->name->chars);
+      printf("%s class", AS_CLASS(value)->name->chars);
       break;
     case OBJ_INSTANCE:
       printf("%s instance", AS_INSTANCE(value)->klass->name->chars);
       break;
+    case OBJ_BOUND_METHOD:
+      printFunction(AS_BOUND_METHOD(value)->method->function);
+      break;
   }
 }
 ObjFunction *newFunction() {
@@ -126,6 +129,7 @@ ObjUpvalue *newUpvalue(Value *slot) {
 ObjClass *newClass(ObjString *name) {
   ObjClass *klass = ALLOCATE_OBJ(ObjClass, OBJ_CLASS);
   klass->name = name;
+  initTable(&klass->methods);
   return klass;
 }
 ObjInstance *newInstance(ObjClass *klass) {
@@ -134,6 +138,12 @@ ObjInstance *newInstance(ObjClass *klass) {
   initTable(&instance->fields);
   return instance;
 }
+ObjBoundMethod *newBoundMethod(Value receiver, ObjClosure *method) {
+  ObjBoundMethod *bound = ALLOCATE_OBJ(ObjBoundMethod, OBJ_BOUND_METHOD);
+  bound->receiver = receiver;
+  bound->method = method;
+  return bound;
+}
 /// 分配内存空间
 /// \param size 空间大小
 /// \param type 对象类型
@@ -147,7 +157,7 @@ static Obj *allocateObject(size_t size, ObjType type) {
   vm.objects = object;
 
 #ifdef DEBUG_LOG_GC
-  printf("%p allocate %zu for %d\n", (void *) object, size, type);
+  PRINTLNF("%p allocate %zu for %d\n", (void *) object, size, type);
 #endif
 
   return object;

+ 11 - 0
object.h

@@ -18,6 +18,7 @@
 
 #define OBJ_TYPE(value) (AS_OBJ(value)->type)
 
+#define IS_BOUND_METHOD(value) isObjType(value, OBJ_BOUND_METHOD)
 #define IS_CLASS(value) isObjType(value, OBJ_CLASS)
 #define IS_CLOSURE(value) isObjType(value, OBJ_CLOSURE)
 #define IS_FUNCTION(value) isObjType(value, OBJ_FUNCTION)
@@ -25,6 +26,7 @@
 #define IS_NATIVE(value) isObjType(value, OBJ_NATIVE)
 #define IS_STRING(value) isObjType(value, OBJ_STRING)
 
+#define AS_BOUND_METHOD(value) ((ObjBoundMethod *) AS_OBJ(value))
 #define AS_CLASS(value) ((ObjClass *) AS_OBJ(value))
 #define AS_CLOSURE(value) ((ObjClosure *) AS_OBJ(value))
 #define AS_FUNCTION(value) ((ObjFunction *) AS_OBJ(value))
@@ -34,6 +36,7 @@
 #define AS_CSTRING(value) (((ObjString *) AS_OBJ(value))->chars)
 
 typedef enum {
+  OBJ_BOUND_METHOD,
   OBJ_CLASS,
   OBJ_CLOSURE,
   OBJ_NATIVE,
@@ -88,6 +91,7 @@ typedef struct {
 typedef struct {
   struct Obj obj;
   ObjString *name;
+  Table methods;
 } ObjClass;
 
 typedef struct {
@@ -96,6 +100,13 @@ typedef struct {
   Table fields;
 } ObjInstance;
 
+typedef struct {
+  struct Obj obj;
+  Value receiver;
+  ObjClosure *method;
+} ObjBoundMethod;
+
+ObjBoundMethod *newBoundMethod(Value receiver, ObjClosure *method);
 ObjClass *newClass(ObjString *name);
 ObjClosure *newClosure(ObjFunction *function);
 ObjFunction *newFunction();

+ 4 - 0
test/local.lox

@@ -0,0 +1,4 @@
+{
+  var a = "first";
+  var b = "second";
+}

+ 8 - 0
test/method.lox

@@ -0,0 +1,8 @@
+class Scone {
+  topping(first, second) {
+    print "scone with " + first + " and " + second;
+  }
+}
+
+var scone = Scone();
+scone.topping("berries", "cream");

+ 12 - 0
test/this.lox

@@ -0,0 +1,12 @@
+// This program should print “Nested instance”
+class Nested {
+  method() {
+    fun function() {
+      print this;
+    }
+
+    function();
+  }
+}
+
+Nested().method();

+ 37 - 6
vm.c

@@ -98,6 +98,11 @@ static bool call(ObjClosure *closure, int argCount) {
 static bool callValue(Value callee, int argCount) {
   if (IS_OBJ(callee)) {
     switch (OBJ_TYPE(callee)) {
+      case OBJ_BOUND_METHOD: {
+        ObjBoundMethod *bound = AS_BOUND_METHOD(callee);
+        vm.stackTop[-argCount-1] = bound->receiver;
+        return call(bound->method, argCount);
+      }
       case OBJ_CLOSURE:
         return call(AS_CLOSURE(callee), argCount);
       case OBJ_NATIVE: {
@@ -168,6 +173,23 @@ static void closeUpvalues(Value *last) {
   }
 }
 
+static void defineMethod(ObjString *name) {
+  Value method = peek(0);
+  ObjClass *klass = AS_CLASS(peek(1));
+  tableSet(&klass->methods, name, method);
+  pop();
+}
+static bool bindKlassMethod(ObjClass *klass, ObjString *name) {
+  Value method;
+  if (!tableGet(&klass->methods, name, &method)) {
+    runtimeError("Undefined property '%s'.", name->chars);
+    return false;
+  }
+  ObjBoundMethod *bound = newBoundMethod(peek(0), AS_CLOSURE(method));
+  pop();
+  push(OBJ_VAL(bound));
+  return true;
+}
 /// VM run function - exec opcode
 /// \return
 static InterpretResult run() {
@@ -253,6 +275,10 @@ static InterpretResult run() {
         push(value);
         break;
       }
+      case OP_METHOD: {
+        defineMethod(READ_STRING());
+        break;
+      }
       case OP_SET_GLOBAL: {
         ObjString *name = READ_STRING();
         if (tableSet(&vm.globals, name, peek(0))) {
@@ -318,7 +344,9 @@ static InterpretResult run() {
         BINARY_OP(BOOL_VAL, <);
         break;
       case OP_PRINT: {
-        printValue(pop());
+        Value value = pop();
+        printf("****PRINT**** ");
+        printValue(value);
         printf("\n");
         break;
       }
@@ -360,8 +388,11 @@ static InterpretResult run() {
           push(value);
           break;
         }
-        runtimeError("Undefined property '%s'.", name->chars);
-        return INTERPRET_RUNTIME_ERROR;
+
+        if (!bindKlassMethod(instance->klass, name)) {
+          return INTERPRET_RUNTIME_ERROR;
+        }
+        break;
       }
       case OP_SET_PROPERTY: {
         if (!IS_INSTANCE(peek(1))) {
@@ -459,7 +490,7 @@ void push(Value value) {
   *vm.stackTop = value;
   vm.stackTop++;
 #ifdef DEBUG_TRACE_EXECUTION
-  printf("push stack: ");
+  printf(">> push stack: ");
   printValue(value);
   printf("\n");
 #endif
@@ -468,9 +499,9 @@ void push(Value value) {
 Value pop() {
   vm.stackTop--;
   //! NOTE: 出栈后,里面的值没有清除
-  Value value =  *vm.stackTop;
+  Value value = *vm.stackTop;
 #ifdef DEBUG_TRACE_EXECUTION
-  printf("pop stack: ");
+  printf("<< pop stack: ");
   printValue(value);
   printf("\n");
 #endif