瀏覽代碼

function calls statement

runningwater 2 年之前
父節點
當前提交
7a237ba9a5
共有 5 個文件被更改,包括 139 次插入35 次删除
  1. 1 0
      chunk.h
  2. 38 1
      compiler.c
  3. 9 2
      debug.c
  4. 16 15
      test/function.lox
  5. 75 17
      vm.c

+ 1 - 0
chunk.h

@@ -39,6 +39,7 @@ typedef enum {
   OP_JUMP_IF_FALSE,/// <OP_JUMP_IF_FALSE + +>
   OP_JUMP,         /// <OP_JUMP + +> 往后跳
   OP_LOOP,         /// <OP_LOOP ++>  往前跳
+  OP_CALL,         /// <OP_CALL argCount>
   OP_RETURN,       ///<OP_RETURN>
 } OpCode;
 

+ 38 - 1
compiler.c

@@ -159,6 +159,7 @@ static int emitJump(uint8_t instruction) {
   return currentChunk()->count - 2;
 }
 static void emitReturn() {
+  emitByte(OP_NIL);
   emitByte(OP_RETURN);
 }
 static uint8_t makeConstant(Value value) {
@@ -298,6 +299,19 @@ static void forStatement() {
   }
   endScope();
 }
+static void returnStatement() {
+  if (current->type == TYPE_SCRIPT) {
+    error("Can't return from top-level code.");
+  }
+
+  if (match(TOKEN_SEMICOLON)) {
+    emitReturn();
+  } else {
+    expression();
+    consume(TOKEN_SEMICOLON, "Expect ';' after return value.");
+    emitByte(OP_RETURN);
+  }
+}
 static void ifStatement() {
   consume(TOKEN_LEFT_PAREN, "Expect '(' after 'if'.");
   expression();
@@ -391,6 +405,8 @@ static void statement() {
     forStatement();
   } else if (match(TOKEN_IF)) {
     ifStatement();
+  } else if (match(TOKEN_RETURN)) {
+    returnStatement();
   } else if (match(TOKEN_WHILE)) {
     whileStatement();
   } else if (match(TOKEN_LEFT_BRACE)) {
@@ -584,6 +600,27 @@ static void binary(__attribute__((unused)) bool canAssign) {
     default: return;// Unreachable.
   }
 }
+static uint8_t argumentList() {
+  uint8_t argCount = 0;
+  if (!check(TOKEN_RIGHT_PAREN)) {
+    do {
+      expression();
+      if (argCount == 255) error("Can't have more than 255 arguments.");
+      argCount++;
+    } while (match(TOKEN_COMMA));
+  }
+  consume(TOKEN_RIGHT_PAREN, "Expect ')' after arguments.");
+  return argCount;
+}
+/// Function call expression is kind of an infix ( operator.
+/// You have a high-precedence expression on the left for the thing
+/// being called - usually just a single identifier.
+/// Then the ( in the middle, followed by the argument expressions
+/// separated by commas, and a final ) to wrap it up at the end.
+static void call(__attribute__((unused)) bool canAssign) {
+  uint8_t argCount = argumentList();
+  emitBytes(OP_CALL, argCount);
+}
 static void literal(__attribute__((unused)) bool canAssign) {
   switch (parser.previous.type) {
     case TOKEN_FALSE:
@@ -599,7 +636,7 @@ static void literal(__attribute__((unused)) bool canAssign) {
   }
 }
 ParseRule rules[] = {
-    [TOKEN_LEFT_PAREN] = {grouping, NULL, PREC_NONE},
+    [TOKEN_LEFT_PAREN] = {grouping, call, PREC_CALL},
     [TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE},
     [TOKEN_LEFT_BRACE] = {NULL, NULL, PREC_NONE},
     [TOKEN_RIGHT_BRACE] = {NULL, NULL, PREC_NONE},

+ 9 - 2
debug.c

@@ -7,6 +7,7 @@
 #include <stdio.h>
 static int constantInstruction(const char *, Chunk *, int);
 static int simpleInstruction(const char *name, int offset);
+static int byteInstruction(const char *name, Chunk *chunk, int offset);
 
 void disassembleChunk(Chunk *chunk, const char *name) {
   printf("== %s STARTING ==\n", name);
@@ -41,8 +42,8 @@ int disassembleInstruction(Chunk *chunk, int offset) {
     case OP_DEFINE_GLOBAL: return constantInstruction("OP_DEFINE_GLOBAL", chunk, offset);
     case OP_GET_GLOBAL: return constantInstruction("OP_GET_GLOBAL", chunk, offset);
     case OP_SET_GLOBAL: return constantInstruction("OP_SET_GLOBAL", chunk, offset);
-    case OP_GET_LOCAL: return constantInstruction("OP_GET_LOCAL", chunk, offset);
-    case OP_SET_LOCAL: return constantInstruction("OP_SET_LOCAL", chunk, offset);
+    case OP_GET_LOCAL: return byteInstruction("OP_GET_LOCAL", chunk, offset);
+    case OP_SET_LOCAL: return byteInstruction("OP_SET_LOCAL", chunk, offset);
     case OP_ADD: return simpleInstruction("OP_ADD", offset);
     case OP_SUBTRACT: return simpleInstruction("OP_SUBTRACT", offset);
     case OP_MULTIPLY: return simpleInstruction("OP_MULTIPLY", offset);
@@ -55,6 +56,7 @@ int disassembleInstruction(Chunk *chunk, int offset) {
     case OP_PRINT: return simpleInstruction("OP_PRINT", offset);
     case OP_JUMP: return jumpInstruction("OP_JUMP", 1, chunk, offset);
     case OP_LOOP: return jumpInstruction("OP_LOOP", -1, chunk, offset);
+    case OP_CALL: return byteInstruction("OP_CALL", chunk, offset);
     case OP_JUMP_IF_FALSE: return jumpInstruction("OP_JUMP_IF_FALSE", 1, chunk, offset);
     case OP_RETURN: return simpleInstruction("OP_RETURN", offset);
     default:
@@ -85,4 +87,9 @@ static int constantInstruction(const char *name, Chunk *chunk, int offset) {
 static int simpleInstruction(const char *name, int offset) {
   printf("%s\n", name);
   return offset + 1;
+}
+static int byteInstruction(const char *name, Chunk *chunk, int offset) {
+  uint8_t slot = chunk->code[offset + 1];
+  printf("%-16s %4d\n", name, slot);
+  return offset + 2;
 }

+ 16 - 15
test/function.lox

@@ -1,18 +1,19 @@
-// fun first() {
-//   var a = 1;
-//   second();
-//   var b = 2;
-// }
-//
-// fun second() {
-//   var c = 3;
-//   var d = 4;
-// }
-//
-// first();
+fun first() {
+  var a = 1;
+  second();
+  var b = 2;
+}
 
-fun areWeHavingItYet() {
-  print "Yes we are!";
+fun second() {
+  var c = 3;
+  var d = 4;
 }
 
-print areWeHavingItYet;
+first();
+
+//  fun sum(a, b, c) {
+//    return a + b + c;
+// }
+//
+// print 4 + sum(5, 6, 7);
+

+ 75 - 17
vm.c

@@ -28,11 +28,19 @@ static void runtimeError(const char *format, ...) {
   vfprintf(stderr, format, args);
   va_end(args);
   fputs("\n", stderr);
+  for (int i = vm.frameCount - 1; i >= 0; i--) {
+    CallFrame *frame = &vm.frames[i];
+    ObjFunction *function = frame->function;
+    size_t instruction = frame->ip - frame->function->chunk.code - 1;
+    int line = function->chunk.lines[instruction];
+    fprintf(stderr, "[line %d] in ", line);
+    if (function->name == NULL) {
+      fprintf(stderr, "script\n");
+    } else {
+      fprintf(stderr, "%s()\n", function->name->chars);
+    }
+  }
 
-  CallFrame *frame = &vm.frames[vm.frameCount - 1];
-  size_t instruction = frame->ip - frame->function->chunk.code - 1;
-  int line = frame->function->chunk.lines[instruction];
-  fprintf(stderr, "[line %d] in script\n", line);
   resetStack();
 }
 
@@ -46,6 +54,36 @@ void initVM() {
 static Value peek(int distance) {
   return vm.stackTop[-1 - distance];
 }
+static bool call(ObjFunction *function, int argCount) {
+  if (argCount != function->arity) {
+    runtimeError("Expected %d arguments but got %d", function->arity, argCount);
+    return false;
+  }
+
+  if (vm.frameCount == FRAMES_MAX) {
+    runtimeError("Stack overflow.");
+    return false;
+  }
+
+  CallFrame *frame = &vm.frames[vm.frameCount++];
+  frame->function = function;
+  frame->ip = function->chunk.code;
+  frame->slots = vm.stackTop - argCount - 1;
+  return true;
+}
+static bool callValue(Value callee, int argCount) {
+  if (IS_OBJ(callee)) {
+    switch (OBJ_TYPE(callee)) {
+      case OBJ_FUNCTION:
+        return call(AS_FUNCTION(callee), argCount);
+      default:
+        break;// Non-callable object type.
+    }
+  }
+
+  runtimeError("Can only call functions and classes.");
+  return false;
+}
 static bool isFalse(Value value) {
   // the  nil and false will return true
   return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
@@ -86,12 +124,10 @@ static InterpretResult run() {
     push(valueType(a op b));                          \
   } while (false)
 
-#ifdef DEBUG_TRACE_EXECUTION
-  printf("!!! <Stack tracing> start\n");
-#endif
   for (;;) {
 #ifdef DEBUG_TRACE_EXECUTION
     //! <Stack tracing> start
+    printf("\n!!! <Stack tracing now>:\n");
     printf("------------------------\n|");
     for (Value *slot = vm.stack; slot < vm.stackTop; slot++) {
       printf("[ ");
@@ -99,11 +135,16 @@ static InterpretResult run() {
       printf(" ]");
     }
     printf("\n------------------------");
+
+    printf("\n");
     printf("\n");
     //! <Stack tracing> end
+
+    printf("The Instruction: \n");
     disassembleInstruction(&frame->function->chunk, (int) (frame->ip - frame->function->chunk.code));
 #endif
-    switch (READ_BYTE()) {
+    uint8_t opCode = READ_BYTE();
+    switch (opCode) {
       case OP_CONSTANT: {
         Value constant = READ_CONSTANT();
         push(constant);
@@ -221,14 +262,30 @@ static InterpretResult run() {
         frame->ip -= offset;
         break;
       }
+      case OP_CALL: {
+        int argCount = READ_BYTE();
+        if (!callValue(peek(argCount), argCount)) {
+          return INTERPRET_RUNTIME_ERROR;
+        }
+        frame = &vm.frames[vm.frameCount - 1];
+        break;
+      }
       case OP_RETURN: {
-        // Exit interpreter.
-        return INTERPRET_OK;
+        Value result = pop();
+        vm.frameCount--;
+        if (vm.frameCount == 0) {
+          pop();
+          return INTERPRET_OK;
+        }
+
+        vm.stackTop = frame->slots;
+        push(result);
+        frame = &vm.frames[vm.frameCount - 1];
+        break;
       }
+      default:
+        break;
     }
-#ifdef DEBUG_TRACE_EXECUTION
-    printf("!!! <Stack tracing> end\n");
-#endif
   }
 
 #undef READ_BYTE
@@ -250,10 +307,11 @@ InterpretResult interpret(const char *source) {
 
   push(OBJ_VAL(function));
 
-  CallFrame *frame = &vm.frames[vm.frameCount++];
-  frame->function = function;
-  frame->ip = function->chunk.code;
-  frame->slots = vm.stack;
+  //  CallFrame *frame = &vm.frames[vm.frameCount++];
+  //  frame->function = function;
+  //  frame->ip = function->chunk.code;
+  //  frame->slots = vm.stack;
+  call(function, 0);
 
   return run();
 }