Explorar el Código

super class finished

runningwater hace 2 años
padre
commit
04c944ffcd
Se han modificado 6 ficheros con 144 adiciones y 1 borrados
  1. 3 0
      chunk.h
  2. 56 1
      compiler.c
  3. 3 0
      debug.c
  4. 11 0
      test/inheriting-method.lox
  5. 39 0
      test/super.lox
  6. 32 0
      vm.c

+ 3 - 0
chunk.h

@@ -48,8 +48,11 @@ typedef enum {
   OP_SET_PROPERTY,
   OP_GET_PROPERTY,
   OP_INVOKE,
+  OP_SUPER_INVOKE,
+  OP_INHERIT,
   OP_CLASS,
   OP_METHOD,
+  OP_GET_SUPER,
 } OpCode;
 
 //============================================================================

+ 56 - 1
compiler.c

@@ -73,6 +73,7 @@ typedef struct Compiler {
 
 typedef struct ClassCompiler {
   struct ClassCompiler *enclosing;
+  bool hasSuperclass;
 } ClassCompiler;
 
 Parser parser;
@@ -90,6 +91,7 @@ static void statement();
 static void declaration();
 static void markInitialized();
 static void namedVariable(Token name, bool canAssign);
+static void addLocal(Token name);
 
 static void initCompiler(Compiler *compiler, FunctionType type);
 
@@ -98,6 +100,8 @@ static void endScope();
 
 static void block();
 static ObjFunction *endCompiler();
+static void variable(bool);
+static uint8_t argumentList();
 
 static Chunk *currentChunk() {
   return &current->function->chunk;
@@ -249,6 +253,12 @@ static void method() {
 
   emitBytes(OP_METHOD, constant);
 }
+static Token syntheticToken(const char *text) {
+  Token token;
+  token.start = text;
+  token.length = (int) strlen(text);
+  return token;
+}
 static void classDeclaration() {
   consume(TOKEN_IDENTIFIER, "Expect class name.");
   Token className = parser.previous;
@@ -260,8 +270,27 @@ static void classDeclaration() {
 
   ClassCompiler classCompiler;
   classCompiler.enclosing = currentClass;
+  classCompiler.hasSuperclass = false;
   currentClass = &classCompiler;
 
+  // Superclass
+  if (match(TOKEN_LESS)) {
+    consume(TOKEN_IDENTIFIER, "Expect superclass name.");
+    variable(false);
+
+    if (identifiersEqual(&className, &parser.previous)) {
+      error("A class can't inherit from itself.");
+    }
+
+    beginScope();
+    addLocal(syntheticToken("super"));
+    defineVariable(0);
+
+    namedVariable(className, false);
+    emitByte(OP_INHERIT);
+    classCompiler.hasSuperclass = true;
+  }
+
   namedVariable(className, false);
   consume(TOKEN_LEFT_BRACE, "Expect '{' before class body.");
   while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
@@ -270,6 +299,10 @@ static void classDeclaration() {
   consume(TOKEN_RIGHT_BRACE, "Expect '}' after class body.");
   emitByte(OP_POP);
 
+  if (classCompiler.hasSuperclass) {
+    endScope();
+  }
+
   currentClass = currentClass->enclosing;
 }
 static void funDeclaration() {
@@ -673,6 +706,28 @@ static void namedVariable(Token name, bool canAssign) {
 static void variable(bool canAssign) {
   namedVariable(parser.previous, canAssign);
 }
+static void super_(__attribute__((unused)) bool canAssign) {
+  if (currentClass == NULL) {
+    error("Can't use 'super' outside of a class.");
+  } else if (!currentClass->hasSuperclass) {
+    error("Can't use 'super' in a class with no superclass.");
+  }
+
+  consume(TOKEN_DOT, "Expect '.' after 'super'.");
+  consume(TOKEN_IDENTIFIER, "Expect superclass method name.");
+  uint8_t name = identifierConstant(&parser.previous);
+
+  namedVariable(syntheticToken("this"), false);
+  if (match(TOKEN_LEFT_PAREN)) {
+    uint8_t argCount = argumentList();
+    namedVariable(syntheticToken("super"), false);
+    emitBytes(OP_SUPER_INVOKE, name);
+    emitByte(argCount);
+  } else {
+    namedVariable(syntheticToken("super"), false);
+    emitBytes(OP_GET_SUPER, name);
+  }
+}
 static void this_(__attribute__((unused)) bool canAssign) {
   if (currentClass == NULL) {
     error("Can't use 'this' outside of a class.");
@@ -824,7 +879,7 @@ ParseRule rules[] = {
     [TOKEN_OR] = {NULL, or_, PREC_OR},
     [TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
     [TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
-    [TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
+    [TOKEN_SUPER] = {super_, NULL, PREC_NONE},
     [TOKEN_THIS] = {this_, NULL, PREC_NONE},
     [TOKEN_TRUE] = {literal, NULL, PREC_NONE},
     [TOKEN_VAR] = {NULL, NULL, PREC_NONE},

+ 3 - 0
debug.c

@@ -91,6 +91,9 @@ int disassembleInstruction(Chunk *chunk, int 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_INHERIT: return simpleInstruction("OP_INHERIT", offset);
+    case OP_GET_SUPER: return constantInstruction("OP_GET_SUPER", chunk, offset);
+    case OP_SUPER_INVOKE: return invokeInstruction("OP_SUPER_INVOKE", chunk, offset);
     case OP_RETURN: return simpleInstruction("OP_RETURN", offset);
     default:
       printf("Unknown opcode %d\n", instruction);

+ 11 - 0
test/inheriting-method.lox

@@ -0,0 +1,11 @@
+class Doughnut {
+  cook() {
+    print "Dunk in the fryer.";
+  }
+}
+
+class Cruller < Doughnut {
+  finish() {
+    print "Glaze with icing.";
+  }
+}

+ 39 - 0
test/super.lox

@@ -0,0 +1,39 @@
+class A {
+  method() {
+    print "A method";
+  }
+}
+
+class B < A {
+  method() {
+    print "B method";
+  }
+
+  test() {
+    super.method();
+  }
+}
+
+class C < B {}
+
+C().test();
+
+// ==================================================
+
+class Doughnut {
+  cook() {
+    print "Dunk in the fryer.";
+    this.finish("sprinkles");
+  }
+
+  finish(ingredient) {
+    print "Finish with " + ingredient;
+  }
+}
+
+class Cruller < Doughnut {
+  finish(ingredient) {
+    // No sprinkles, always icing.
+    super.finish("icing");
+  }
+}

+ 32 - 0
vm.c

@@ -458,6 +458,18 @@ static InterpretResult run() {
         frame = &vm.frames[vm.frameCount - 1];
         break;
       }
+      case OP_INHERIT: {
+        Value superclass = peek(1);
+        if (!IS_CLASS(superclass)) {
+          runtimeError("Superclass must be a class.");
+          return INTERPRET_RUNTIME_ERROR;
+        }
+
+        ObjClass *subclass = AS_CLASS(peek(0));
+        tableAddAll(&AS_CLASS(superclass)->methods, &subclass->methods);
+        pop();// Subclass.
+        break;
+      }
       case OP_CLOSURE: {
         ObjFunction *function = AS_FUNCTION(READ_CONSTANT());
         ObjClosure *closure = newClosure(function);
@@ -487,6 +499,26 @@ static InterpretResult run() {
         closeUpvalues(vm.stackTop - 1);
         pop();
         break;
+      case OP_SUPER_INVOKE: {
+        ObjString *method = READ_STRING();
+        int argCount = READ_BYTE();
+        ObjClass *superclass = AS_CLASS(pop());
+        if (!invokeFromClas(superclass, method, argCount)) {
+          return INTERPRET_RUNTIME_ERROR;
+        }
+        frame = &vm.frames[vm.frameCount - 1];
+
+        break;
+      }
+      case OP_GET_SUPER: {
+        ObjString *name = READ_STRING();
+        ObjClass *superclass = AS_CLASS(pop());
+
+        if (!bindKlassMethod(superclass, name)) {
+          return INTERPRET_RUNTIME_ERROR;
+        }
+        break;
+      }
       case OP_RETURN: {
         Value result = pop();
         closeUpvalues(frame->slots);