Browse Source

add function declare statement

runningwater 2 years ago
parent
commit
9fd0c5ff5d
8 changed files with 176 additions and 46 deletions
  1. 82 14
      compiler.c
  2. 1 1
      compiler.h
  3. 6 0
      memory.c
  4. 18 2
      object.c
  5. 13 0
      object.h
  6. 18 0
      test/function.lox
  7. 23 23
      vm.c
  8. 15 6
      vm.h

+ 82 - 14
compiler.c

@@ -48,7 +48,16 @@ typedef struct {
   int depth;
 } Local;
 
-typedef struct {
+typedef enum {
+  TYPE_FUNCTION,
+  TYPE_SCRIPT,
+} FunctionType;
+
+typedef struct Compiler {
+  struct Compiler *enclosing;
+  ObjFunction *function;
+  FunctionType type;
+
   Local locals[UINT8_COUNT];
   int localCount;/// how many locals are in scope
   int scopeDepth;
@@ -56,7 +65,6 @@ typedef struct {
 
 Parser parser;
 Compiler *current = NULL;
-Chunk *compilingChunk;
 
 static void parsePrecedence(Precedence);
 static uint8_t parseVariable(const char *);
@@ -66,12 +74,18 @@ static bool identifiersEqual(Token *a, Token *b);
 static ParseRule *getRule(TokenType);
 static void statement();
 static void declaration();
+static void markInitialized();
+
+static void initCompiler(Compiler *compiler, FunctionType type);
 
 static void beginScope();
 static void endScope();
 
+static void block();
+static ObjFunction *endCompiler();
+
 static Chunk *currentChunk() {
-  return compilingChunk;
+  return &current->function->chunk;
 }
 
 static void errorAt(Token *token, const char *message) {
@@ -158,6 +172,38 @@ static uint8_t makeConstant(Value value) {
 static void expression() {
   parsePrecedence(PREC_ASSIGNMENT);
 }
+static void function(FunctionType type) {
+  Compiler compiler;
+  initCompiler(&compiler, type);
+  beginScope();
+
+  consume(TOKEN_LEFT_PAREN, "Expect '(' after function name.");
+
+  ///! Parameters
+  if (!check(TOKEN_RIGHT_PAREN)) {
+    do {
+      current->function->arity++;
+      if (current->function->arity > 255) {
+        errorAtCurrent("Can't have more than 255 parameters.");
+      }
+      uint8_t constant = parseVariable("Expect parameter name.");
+      defineVariable(constant);
+    } while (match(TOKEN_COMMA));
+  }
+  consume(TOKEN_RIGHT_PAREN, "Expect ')' after parameters.");
+  consume(TOKEN_LEFT_BRACE, "Expect '{' before function body.");
+  ///! Body
+  block();
+
+  ObjFunction *fun = endCompiler();
+  emitBytes(OP_CONSTANT, makeConstant(OBJ_VAL(fun)));
+}
+static void funDeclaration() {
+  uint8_t global = parseVariable("Expect function name.");
+  markInitialized();
+  function(TYPE_FUNCTION);
+  defineVariable(global);
+}
 /// for example: var a; var b = 10;
 static void varDeclaration() {
   uint8_t global = parseVariable("Expect variable name.");
@@ -314,7 +360,9 @@ static void synchronize() {
 ///               | varDecl
 ///               | statement ;
 static void declaration() {
-  if (match(TOKEN_VAR)) {
+  if (match(TOKEN_FUN)) {
+    funDeclaration();
+  } else if (match(TOKEN_VAR)) {
     varDeclaration();
   } else {
     statement();
@@ -369,18 +417,35 @@ static void endScope() {
 static void emitConstant(Value value) {
   emitBytes(OP_CONSTANT, makeConstant(value));
 }
-static void initCompiler(Compiler *compiler) {
+static void initCompiler(Compiler *compiler, FunctionType type) {
+  compiler->enclosing = current;// 上一极的 compiler
+  compiler->function = NULL;
+  compiler->type = type;
   compiler->localCount = 0;
   compiler->scopeDepth = 0;
+  compiler->function = newFunction();
   current = compiler;
+  if (type != TYPE_SCRIPT) {
+    ///! Function name
+    current->function->name = copyString(parser.previous.start, parser.previous.length);
+  }
+
+  Local *local = &current->locals[current->localCount++];
+  local->depth = 0;
+  local->name.start = "";
+  local->name.length = 0;
 }
-static void endCompiler() {
+static ObjFunction *endCompiler() {
   emitReturn();
+  ObjFunction *function = current->function;
 #ifdef DEBUG_PRINT_CODE
   if (!parser.hadError) {
-    disassembleChunk(currentChunk(), "code");
+    disassembleChunk(currentChunk(), function->name != NULL ? function->name->chars : "<script>");
   }
 #endif
+
+  current = current->enclosing;// 当前的 compiler 变成上级 compiler
+  return function;
 }
 static void grouping(__attribute__((unused)) bool canAssign) {
   expression();
@@ -647,11 +712,15 @@ static uint8_t parseVariable(const char *errorMessage) {
 
   return identifierConstant(&parser.previous);
 }
+static void markInitialized() {
+  if (current->scopeDepth == 0) return;
+  current->locals[current->localCount - 1].depth = current->scopeDepth;
+}
 static void defineVariable(uint8_t global) {
 
   if (current->scopeDepth > 0) {
     // markInitialized 未初始化时值 为 -1
-    current->locals[current->localCount - 1].depth = current->scopeDepth;
+    markInitialized();
     // 本地变量,直接退出
     return;
   }
@@ -678,11 +747,10 @@ static ParseRule *getRule(TokenType type) {
                    | statement ;
   ******************************************************************************
   */
-bool compile(const char *source, Chunk *chunk) {
-  Compiler compiler;
+ObjFunction *compile(const char *source) {
   initScanner(source);
-  initCompiler(&compiler);
-  compilingChunk = chunk;
+  Compiler compiler;
+  initCompiler(&compiler, TYPE_SCRIPT);
 
   parser.hadError = false;
   parser.panicMode = false;
@@ -693,6 +761,6 @@ bool compile(const char *source, Chunk *chunk) {
     declaration();
   }
 
-  endCompiler();//! return opcode
-  return !parser.hadError;
+  ObjFunction *function = endCompiler();
+  return parser.hadError ? NULL : function;
 }

+ 1 - 1
compiler.h

@@ -15,6 +15,6 @@
 
 /// \brief 编译
 /// \param source 源代码
-bool compile(const char *source, Chunk *chunk);
+ObjFunction* compile(const char *source);
 
 #endif//CLOX__COMPILER_H_

+ 6 - 0
memory.c

@@ -31,6 +31,12 @@ void *reallocate(void *pointer, size_t oldSize, size_t newSize) {
 }
 static void freeObject(Obj *object) {
   switch (object->type) {
+    case OBJ_FUNCTION: {
+      ObjFunction *function = (ObjFunction *) object;
+      freeChunk(&function->chunk);
+      FREE(ObjFunction, object);
+      break;
+    }
     case OBJ_STRING:
       FREE_ARRAY(char, ((ObjString *) object)->chars, ((ObjString *) object)->length + 1);
       FREE(ObjString, object);

+ 18 - 2
object.c

@@ -11,8 +11,8 @@
 #include <string.h>
 
 #include "memory.h"
-#include "table.h"
 #include "object.h"
+#include "table.h"
 #include "vm.h"
 
 static Obj *allocateObject(size_t size, ObjType type);
@@ -42,13 +42,30 @@ ObjString *copyString(const char *chars, int length) {
   heapChars[length] = '\0';
   return allocateString(heapChars, length, hash);
 }
+static void printFunction(ObjFunction *function) {
+  if (function->name == NULL) {
+    printf("<script>");
+    return;
+  }
+  printf("<fn %s>", function->name->chars);
+}
 void printObject(Value value) {
   switch (OBJ_TYPE(value)) {
+    case OBJ_FUNCTION:
+      printFunction(AS_FUNCTION(value));
+      break;
     case OBJ_STRING:
       printf("%s", AS_CSTRING(value));
       break;
   }
 }
+ObjFunction *newFunction() {
+  ObjFunction *function = ALLOCATE_OBJ(ObjFunction, OBJ_FUNCTION);
+  function->arity = 0;
+  function->name = NULL;
+  initChunk(&function->chunk);
+  return function;
+}
 ObjString *takeString(char *chars, int length) {
   uint32_t hash = hashString(chars, length);
   ObjString *interned = tableFindString(&vm.strings, chars, length, hash);
@@ -60,7 +77,6 @@ ObjString *takeString(char *chars, int length) {
 
   return allocateString(chars, length, hash);
 }
-
 static Obj *allocateObject(size_t size, ObjType type) {
   Obj *object = (Obj *) reallocate(NULL, 0, size);
   object->type = type;

+ 13 - 0
object.h

@@ -11,17 +11,22 @@
 #ifndef CLOX_OBJECT_H
 #define CLOX_OBJECT_H
 
+#include "chunk.h"
+#include "common.h"
 #include "value.h"
 
 #define OBJ_TYPE(value) (AS_OBJ(value)->type)
 
+#define IS_FUNCTION(value) isObjType(value, OBJ_FUNCTION);
 #define IS_STRING(value) isObjType(value, OBJ_STRING)
 
+#define AS_FUNCTION(value) ((ObjFunction *) AS_OBJ(value))
 #define AS_STRING(value) ((ObjString *) AS_OBJ(value))
 #define AS_CSTRING(value) (((ObjString *) AS_OBJ(value))->chars)
 
 typedef enum {
   OBJ_STRING,
+  OBJ_FUNCTION,
 } ObjType;
 
 struct Obj {
@@ -29,6 +34,13 @@ struct Obj {
   struct Obj *next;
 };
 
+typedef struct {
+  struct Obj obj;
+  int arity;//  stores the number of parameters the function expects
+  Chunk chunk;
+  ObjString *name;
+} ObjFunction;
+
 struct ObjString {
   struct Obj obj;
   int length;
@@ -36,6 +48,7 @@ struct ObjString {
   uint32_t hash;// 缓存 hash 值
 };
 
+ObjFunction *newFunction();
 ObjString *takeString(char *chars, int length);
 ObjString *copyString(const char *chars, int length);
 void printObject(Value value);

+ 18 - 0
test/function.lox

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

+ 23 - 23
vm.c

@@ -19,6 +19,7 @@ VM vm;
 /// \brief 重置栈
 static void resetStack() {
   vm.stackTop = vm.stack;// 指针指向栈底
+  vm.frameCount = 0;
 }
 
 static void runtimeError(const char *format, ...) {
@@ -28,8 +29,9 @@ static void runtimeError(const char *format, ...) {
   va_end(args);
   fputs("\n", stderr);
 
-  size_t instruction = vm.ip - vm.chunk->code - 1;
-  int line = vm.chunk->lines[instruction];
+  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();
 }
@@ -62,14 +64,16 @@ static void concatenate() {
   push(OBJ_VAL(result));
 }
 static InterpretResult run() {
+  CallFrame *frame = &vm.frames[vm.frameCount - 1];
+
 //! <macro> reads the byte currently pointed at by ip
 //! and then advances the instruction pointer
-#define READ_BYTE() (*vm.ip++)
+#define READ_BYTE() (*frame->ip++)
 //! reads the next byte from the bytecode
 //! treats the resulting number as an index
-#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
+#define READ_CONSTANT() (frame->function->chunk.constants.values[READ_BYTE()])
 #define READ_SHORT() \
-  (vm.ip += 2, (uint16_t) ((vm.ip[-2] << 8) | vm.ip[-1]))
+  (frame->ip += 2, (uint16_t) ((frame->ip[-2] << 8) | frame->ip[-1]))
 #define READ_STRING() AS_STRING(READ_CONSTANT())
 #define BINARY_OP(valueType, op)                      \
   do {                                                \
@@ -97,7 +101,7 @@ static InterpretResult run() {
     printf("\n------------------------");
     printf("\n");
     //! <Stack tracing> end
-    disassembleInstruction(vm.chunk, (int) (vm.ip - vm.chunk->code));
+    disassembleInstruction(&frame->function->chunk, (int) (frame->ip - frame->function->chunk.code));
 #endif
     switch (READ_BYTE()) {
       case OP_CONSTANT: {
@@ -144,12 +148,12 @@ static InterpretResult run() {
       }
       case OP_GET_LOCAL: {
         uint8_t slot = READ_BYTE();
-        push(vm.stack[slot]);
+        push(frame->slots[slot]);
         break;
       }
       case OP_SET_LOCAL: {
         uint8_t slot = READ_BYTE();
-        vm.stack[slot] = peek(0);
+        frame->slots[slot] = peek(0);
         break;
       }
       case OP_ADD: {
@@ -204,17 +208,17 @@ static InterpretResult run() {
       }
       case OP_JUMP_IF_FALSE: {
         uint16_t offset = READ_SHORT();
-        if (isFalse(peek(0))) vm.ip += offset;
+        if (isFalse(peek(0))) frame->ip += offset;
         break;
       }
       case OP_JUMP: {
         uint16_t offset = READ_SHORT();
-        vm.ip += offset;
+        frame->ip += offset;
         break;
       }
       case OP_LOOP: {
         uint16_t offset = READ_SHORT();
-        vm.ip -= offset;
+        frame->ip -= offset;
         break;
       }
       case OP_RETURN: {
@@ -241,21 +245,17 @@ void freeVM() {
 }
 
 InterpretResult interpret(const char *source) {
-  Chunk chunk;
-  initChunk(&chunk);
-
-  if (!compile(source, &chunk)) {
-    freeChunk(&chunk);
-    return INTERPRET_COMPILE_ERROR;
-  }
+  ObjFunction *function = compile(source);
+  if (function == NULL) return INTERPRET_COMPILE_ERROR;
 
-  vm.chunk = &chunk;
-  vm.ip = vm.chunk->code;
+  push(OBJ_VAL(function));
 
-  InterpretResult result = run();
+  CallFrame *frame = &vm.frames[vm.frameCount++];
+  frame->function = function;
+  frame->ip = function->chunk.code;
+  frame->slots = vm.stack;
 
-  freeChunk(&chunk);
-  return result;
+  return run();
 }
 
 void push(Value value) {

+ 15 - 6
vm.h

@@ -11,19 +11,28 @@
 #ifndef CLOX__VM_H_
 #define CLOX__VM_H_
 
-#include "chunk.h"
+#include "object.h"
+#include "compiler.h"
 #include "table.h"
 
-#define STACK_MAX 256
+#define FRAMES_MAX 64
+#define STACK_MAX (FRAMES_MAX * UINT8_COUNT)
 
 typedef struct {
-  Chunk *chunk;
+  ObjFunction *function;
   uint8_t *ip;
+  Value *slots;
+} CallFrame;
+
+typedef struct {
+  CallFrame frames[FRAMES_MAX];
+  int frameCount;
+
   Value stack[STACK_MAX];
   Value *stackTop;// 栈指针
   Table globals;
-  Table strings;  //
-  Obj *objects;   // 管理分配的 heap 内存
+  Table strings;//
+  Obj *objects; // 管理分配的 heap 内存
 } VM;
 typedef enum {
   INTERPRET_OK,
@@ -31,7 +40,7 @@ typedef enum {
   INTERPRET_RUNTIME_ERROR
 } InterpretResult;
 
-extern VM  vm;
+extern VM vm;
 
 void initVM();
 void freeVM();