Browse Source

String values

runningwater 2 years ago
parent
commit
4b11d37eed
13 changed files with 454 additions and 177 deletions
  1. 1 1
      .idea/codeStyles/codeStyleConfig.xml
  2. 12 5
      chunk.h
  3. 103 59
      compiler.c
  4. 2 1
      compiler.h
  5. 10 2
      debug.c
  6. 18 1
      memory.c
  7. 7 0
      memory.h
  8. 54 0
      object.c
  9. 46 0
      object.h
  10. 32 3
      value.c
  11. 23 14
      value.h
  12. 140 88
      vm.c
  13. 6 3
      vm.h

+ 1 - 1
.idea/codeStyles/codeStyleConfig.xml

@@ -1,5 +1,5 @@
 <component name="ProjectCodeStyleConfiguration">
   <state>
-    <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
   </state>
 </component>

+ 12 - 5
chunk.h

@@ -17,11 +17,18 @@
 
 typedef enum {
   OP_CONSTANT,///<OP_CONSTANT (index)+>
-  OP_NEGATE,  /// \brief prefix -
-  OP_ADD, /// \brief +
-  OP_SUBTRACT, /// \brief -
-  OP_MULTIPLY, /// \brief *
-  OP_DIVIDE, /// \brief /
+  OP_NIL,
+  OP_TRUE,
+  OP_FALSE,
+  OP_NOT,   /// \brief print !true; // "false"
+  OP_NEGATE,/// \brief prefix -
+  OP_EQUAL,
+  OP_GREATER,
+  OP_LESS,
+  OP_ADD,     /// \brief +
+  OP_SUBTRACT,/// \brief -
+  OP_MULTIPLY,/// \brief *
+  OP_DIVIDE,  /// \brief /
   OP_RETURN,  ///<OP_RETURN>
 } OpCode;
 

+ 103 - 59
compiler.c

@@ -7,8 +7,8 @@
   * @date           : 2023/8/17
   ******************************************************************************
   */
-#include "common.h"
 #include "compiler.h"
+#include "common.h"
 #include "scanner.h"
 #ifdef DEBUG_PRINT_CODE
 #include "debug.h"
@@ -23,15 +23,15 @@ typedef struct {
 
 typedef enum {
   PREC_NONE,
-  PREC_ASSIGNMENT, // =
-  PREC_OR,    // or
-  PREC_AND,   // and
-  PREC_EQUALITY, // == !=
-  PREC_COMPARISON, // < > <= >=
-  PREC_TERM,  // + -
-  PREC_FACTOR, // * /
-  PREC_UNARY,  // ! -
-  PREC_CALL, // . ()
+  PREC_ASSIGNMENT,// =
+  PREC_OR,        // or
+  PREC_AND,       // and
+  PREC_EQUALITY,  // == !=
+  PREC_COMPARISON,// < > <= >=
+  PREC_TERM,      // + -
+  PREC_FACTOR,    // * /
+  PREC_UNARY,     // ! -
+  PREC_CALL,      // . ()
   PREC_PRIMARY
 } Precedence;
 
@@ -135,6 +135,10 @@ static void number() {
   double value = strtod(parser.previous.start, NULL);
   emitConstant(NUMBER_VAL(value));
 }
+static void string() {
+  emitConstant(OBJ_VAL(copyString(parser.previous.start + 1,
+                                  parser.previous.length - 2)));
+}
 /// Unary negation: -123
 static void unary() {
   TokenType operatorType = parser.previous.type;
@@ -144,9 +148,13 @@ static void unary() {
 
   // Emit the operator instruction.
   switch (operatorType) {
-    case TOKEN_MINUS: emitByte(OP_NEGATE);
+    case TOKEN_BANG:
+      emitByte(OP_NOT);
+      break;
+    case TOKEN_MINUS:
+      emitByte(OP_NEGATE);
       break;
-    default: return; // Unreachable.
+    default: return;// Unreachable.
   }
 }
 /// \brief infix parser
@@ -156,58 +164,94 @@ static void binary() {
   parsePrecedence((Precedence) rule->precedence + 1);
 
   switch (operatorType) {
-    case TOKEN_PLUS: emitByte(OP_ADD);
+    case TOKEN_BANG_EQUAL:
+      emitBytes(OP_EQUAL, OP_NOT);
+      break;
+    case TOKEN_EQUAL_EQUAL:
+      emitByte(OP_EQUAL);
+      break;
+    case TOKEN_GREATER:
+      emitByte(OP_GREATER);
+      break;
+    case TOKEN_GREATER_EQUAL:
+      emitBytes(OP_LESS, OP_NOT);
+      break;
+    case TOKEN_LESS:
+      emitByte(OP_LESS);
       break;
-    case TOKEN_MINUS: emitByte(OP_SUBTRACT);
+    case TOKEN_LESS_EQUAL:
+      emitBytes(OP_GREATER, OP_NOT);
+      break;
+    case TOKEN_PLUS:
+      emitByte(OP_ADD);
+      break;
+    case TOKEN_MINUS:
+      emitByte(OP_SUBTRACT);
+      break;
+    case TOKEN_STAR:
+      emitByte(OP_MULTIPLY);
+      break;
+    case TOKEN_SLASH:
+      emitByte(OP_DIVIDE);
+      break;
+    default: return;// Unreachable.
+  }
+}
+static void literal() {
+  switch (parser.previous.type) {
+    case TOKEN_FALSE:
+      emitByte(OP_FALSE);
       break;
-    case TOKEN_STAR: emitByte(OP_MULTIPLY);
+    case TOKEN_NIL:
+      emitByte(OP_NIL);
       break;
-    case TOKEN_SLASH: emitByte(OP_DIVIDE);
+    case TOKEN_TRUE:
+      emitByte(OP_TRUE);
       break;
-    default: return; // Unreachable.
+    default: return;// Unreachable
   }
 }
 ParseRule rules[] = {
     [TOKEN_LEFT_PAREN] = {grouping, NULL, PREC_NONE},
-    [TOKEN_RIGHT_PAREN]   = {NULL, NULL, PREC_NONE},
-    [TOKEN_LEFT_BRACE]    = {NULL, NULL, PREC_NONE},
-    [TOKEN_RIGHT_BRACE]   = {NULL, NULL, PREC_NONE},
-    [TOKEN_COMMA]         = {NULL, NULL, PREC_NONE},
-    [TOKEN_DOT]           = {NULL, NULL, PREC_NONE},
-    [TOKEN_MINUS]         = {unary, binary, PREC_TERM},
-    [TOKEN_PLUS]          = {NULL, binary, PREC_TERM},
-    [TOKEN_SEMICOLON]     = {NULL, NULL, PREC_NONE},
-    [TOKEN_SLASH]         = {NULL, binary, PREC_FACTOR},
-    [TOKEN_STAR]          = {NULL, binary, PREC_FACTOR},
-    [TOKEN_BANG]          = {NULL, NULL, PREC_NONE},
-    [TOKEN_BANG_EQUAL]    = {NULL, NULL, PREC_NONE},
-    [TOKEN_EQUAL]         = {NULL, NULL, PREC_NONE},
-    [TOKEN_EQUAL_EQUAL]   = {NULL, NULL, PREC_NONE},
-    [TOKEN_GREATER]       = {NULL, NULL, PREC_NONE},
-    [TOKEN_GREATER_EQUAL] = {NULL, NULL, PREC_NONE},
-    [TOKEN_LESS]          = {NULL, NULL, PREC_NONE},
-    [TOKEN_LESS_EQUAL]    = {NULL, NULL, PREC_NONE},
-    [TOKEN_IDENTIFIER]    = {NULL, NULL, PREC_NONE},
-    [TOKEN_STRING]        = {NULL, NULL, PREC_NONE},
-    [TOKEN_NUMBER]        = {number, NULL, PREC_NONE},
-    [TOKEN_AND]           = {NULL, NULL, PREC_NONE},
-    [TOKEN_CLASS]         = {NULL, NULL, PREC_NONE},
-    [TOKEN_ELSE]          = {NULL, NULL, PREC_NONE},
-    [TOKEN_FALSE]         = {NULL, NULL, PREC_NONE},
-    [TOKEN_FOR]           = {NULL, NULL, PREC_NONE},
-    [TOKEN_FUN]           = {NULL, NULL, PREC_NONE},
-    [TOKEN_IF]            = {NULL, NULL, PREC_NONE},
-    [TOKEN_NIL]           = {NULL, NULL, PREC_NONE},
-    [TOKEN_OR]            = {NULL, NULL, PREC_NONE},
-    [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_TRUE]          = {NULL, NULL, PREC_NONE},
-    [TOKEN_VAR]           = {NULL, NULL, PREC_NONE},
-    [TOKEN_WHILE]         = {NULL, NULL, PREC_NONE},
-    [TOKEN_ERROR]         = {NULL, NULL, PREC_NONE},
-    [TOKEN_EOF]           = {NULL, NULL, PREC_NONE},
+    [TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE},
+    [TOKEN_LEFT_BRACE] = {NULL, NULL, PREC_NONE},
+    [TOKEN_RIGHT_BRACE] = {NULL, NULL, PREC_NONE},
+    [TOKEN_COMMA] = {NULL, NULL, PREC_NONE},
+    [TOKEN_DOT] = {NULL, NULL, PREC_NONE},
+    [TOKEN_MINUS] = {unary, binary, PREC_TERM},
+    [TOKEN_PLUS] = {NULL, binary, PREC_TERM},
+    [TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE},
+    [TOKEN_SLASH] = {NULL, binary, PREC_FACTOR},
+    [TOKEN_STAR] = {NULL, binary, PREC_FACTOR},
+    [TOKEN_BANG] = {unary, NULL, PREC_NONE},
+    [TOKEN_BANG_EQUAL] = {NULL, binary, PREC_EQUALITY},
+    [TOKEN_EQUAL] = {NULL, NULL, PREC_NONE},
+    [TOKEN_EQUAL_EQUAL] = {NULL, binary, PREC_EQUALITY},
+    [TOKEN_GREATER] = {NULL, binary, PREC_COMPARISON},
+    [TOKEN_GREATER_EQUAL] = {NULL, binary, PREC_COMPARISON},
+    [TOKEN_LESS] = {NULL, binary, PREC_COMPARISON},
+    [TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON},
+    [TOKEN_IDENTIFIER] = {NULL, NULL, PREC_NONE},
+    [TOKEN_STRING] = {string, NULL, PREC_NONE},
+    [TOKEN_NUMBER] = {number, NULL, PREC_NONE},
+    [TOKEN_AND] = {NULL, NULL, PREC_NONE},
+    [TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
+    [TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
+    [TOKEN_FALSE] = {literal, NULL, PREC_NONE},
+    [TOKEN_FOR] = {NULL, NULL, PREC_NONE},
+    [TOKEN_FUN] = {NULL, NULL, PREC_NONE},
+    [TOKEN_IF] = {NULL, NULL, PREC_NONE},
+    [TOKEN_NIL] = {literal, NULL, PREC_NONE},
+    [TOKEN_OR] = {NULL, NULL, PREC_NONE},
+    [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_TRUE] = {literal, NULL, PREC_NONE},
+    [TOKEN_VAR] = {NULL, NULL, PREC_NONE},
+    [TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
+    [TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
+    [TOKEN_EOF] = {NULL, NULL, PREC_NONE},
 };
 
 /// \brief 优先级处理
@@ -220,12 +264,12 @@ static void parsePrecedence(Precedence precedence) {
     return;
   }
 
-  prefixRule(); ///! 执行具体函数
+  prefixRule();///! 执行具体函数
 
   while (precedence <= getRule(parser.current.type)->precedence) {
     advance();
     ParseFn infixRule = getRule(parser.previous.type)->infix;
-    infixRule(); ///! 执行具体函数
+    infixRule();///! 执行具体函数
   }
 }
 static ParseRule *getRule(TokenType type) {
@@ -241,6 +285,6 @@ bool compile(const char *source, Chunk *chunk) {
   advance();
   expression();
   consume(TOKEN_EOF, "Expect end of expression");
-  endCompiler(); //! return opcode
+  endCompiler();//! return opcode
   return !parser.hadError;
 }

+ 2 - 1
compiler.h

@@ -10,10 +10,11 @@
 
 #ifndef CLOX__COMPILER_H_
 #define CLOX__COMPILER_H_
+#include "object.h"
 #include "vm.h"
 
 /// \brief 编译
 /// \param source 源代码
 bool compile(const char *source, Chunk *chunk);
 
-#endif //CLOX__COMPILER_H_
+#endif//CLOX__COMPILER_H_

+ 10 - 2
debug.c

@@ -3,8 +3,8 @@
 //
 
 #include "debug.h"
-#include <stdio.h>
 #include "value.h"
+#include <stdio.h>
 static int constantInstruction(const char *, Chunk *, int);
 static int simpleInstruction(const char *name, int offset);
 
@@ -28,13 +28,21 @@ int disassembleInstruction(Chunk *chunk, int offset) {
   uint8_t instruction = chunk->code[offset];
   switch (instruction) {
     case OP_CONSTANT: return constantInstruction("OP_CONSTANT", chunk, offset);
+    case OP_NIL: return simpleInstruction("OP_NIL", offset);
+    case OP_TRUE: return simpleInstruction("OP_TRUE", offset);
+    case OP_FALSE: return simpleInstruction("OP_FALSE", 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);
     case OP_DIVIDE: return simpleInstruction("OP_DIVIDE", offset);
+    case OP_NOT: return simpleInstruction("OP_NOT", offset);
     case OP_NEGATE: return simpleInstruction("OP_NEGATE", offset);
+    case OP_EQUAL: return simpleInstruction("OP_EQUAL", offset);
+    case OP_GREATER: return simpleInstruction("OP_GREATER", offset);
+    case OP_LESS: return simpleInstruction("OP_LESS", offset);
     case OP_RETURN: return simpleInstruction("OP_RETURN", offset);
-    default:printf("Unknown opcode %d\n", instruction);
+    default:
+      printf("Unknown opcode %d\n", instruction);
       return offset + 1;
   }
 }

+ 18 - 1
memory.c

@@ -1,4 +1,5 @@
 #include "memory.h"
+#include "vm.h"
 #include <stdlib.h>
 
 //    oldSize	 newSize	               Operation
@@ -27,4 +28,20 @@ void *reallocate(void *pointer, size_t oldSize, size_t newSize) {
   void *result = realloc(pointer, newSize);
   if (result == NULL) exit(1);
   return result;
-}
+}
+static void freeObject(Obj *object) {
+  switch (object->type) {
+    case OBJ_STRING:
+      FREE_ARRAY(char, ((ObjString *) object)->chars, ((ObjString *) object)->length + 1);
+      FREE(ObjString, object);
+      break;
+  }
+}
+void freeObjects() {
+  Obj *object = vm.objects;
+  while (object != NULL) {
+    Obj *next = object->next;
+    freeObject(object);
+    object = next;
+  }
+}

+ 7 - 0
memory.h

@@ -6,6 +6,7 @@
 #define CLOX_MEMORY_H
 
 #include "common.h"
+#include "object.h"
 
 #define GROW_CAPACITY(capacity) \
   ((capacity) < 8 ? 8 : (capacity) *2)
@@ -16,10 +17,16 @@
 #define FREE_ARRAY(type, pointer, oldCount) \
   reallocate(pointer, sizeof(type) * (oldCount), 0)
 
+#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
+
+#define ALLOCATE(type, count) \
+  (type *) reallocate(NULL, 0, sizeof(type) * (count))
+
 //    oldSize	 newSize	               Operation
 //    0	        Non‑zero	            Allocate new block.
 //    Non‑zero	0	                    Free allocation.
 //    Non‑zero	Smaller than oldSize	Shrink existing allocation.
 //    Non‑zero	Larger than oldSize	    Grow existing allocation.
 void *reallocate(void *pointer, size_t oldSize, size_t newSize);
+void freeObjects();
 #endif//CLOX_MEMORY_H

+ 54 - 0
object.c

@@ -0,0 +1,54 @@
+/**
+  ******************************************************************************
+  * @file           : object.cpp
+  * @author         : simon
+  * @brief          : None
+  * @attention      : None
+  * @date           : 2023/8/23
+  ******************************************************************************
+  */
+#include <stdio.h>
+#include <string.h>
+
+#include "memory.h"
+#include "object.h"
+#include "vm.h"
+
+static Obj *allocateObject(size_t size, ObjType type);
+
+#define ALLOCATE_OBJ(type, objectType) \
+  (type *) allocateObject(sizeof(type), objectType)
+
+static ObjString *allocateString(char *chars, int length) {
+  ObjString *string = ALLOCATE_OBJ(ObjString, OBJ_STRING);
+  string->length = length;
+  string->chars = chars;
+  return string;
+}
+
+ObjString *copyString(const char *chars, int length) {
+  char *heapChars = ALLOCATE(char, length + 1);
+  memcpy(heapChars, chars, length);
+  heapChars[length] = '\0';
+  return allocateString(heapChars, length);
+}
+void printObject(Value value) {
+  switch (OBJ_TYPE(value)) {
+    case OBJ_STRING:
+      printf("%s", AS_CSTRING(value));
+      break;
+  }
+}
+ObjString *takeString(char *chars, int length) {
+  return allocateString(chars, length);
+}
+
+static Obj *allocateObject(size_t size, ObjType type) {
+  Obj *object = (Obj *) reallocate(NULL, 0, size);
+  object->type = type;
+
+  object->next = vm.objects;
+  vm.objects = object;
+
+  return object;
+}

+ 46 - 0
object.h

@@ -0,0 +1,46 @@
+/**
+  ******************************************************************************
+  * @file           : object.h
+  * @author         : simon
+  * @brief          : None
+  * @attention      : None
+  * @date           : 2023/8/23
+  ******************************************************************************
+  */
+
+#ifndef CLOX_OBJECT_H
+#define CLOX_OBJECT_H
+
+#include "value.h"
+
+#define OBJ_TYPE(value) (AS_OBJ(value)->type)
+
+#define IS_STRING(value) isObjType(value, OBJ_STRING)
+
+#define AS_STRING(value) ((ObjString *) AS_OBJ(value))
+#define AS_CSTRING(value) (((ObjString *) AS_OBJ(value))->chars)
+
+typedef enum {
+  OBJ_STRING,
+} ObjType;
+
+struct Obj {
+  ObjType type;
+  struct Obj *next;
+};
+
+struct ObjString {
+  struct Obj obj;
+  int length;
+  char *chars;
+};
+
+ObjString *takeString(char *chars, int length);
+ObjString *copyString(const char *chars, int length);
+void printObject(Value value);
+
+static inline bool isObjType(Value value, ObjType type) {
+  return IS_OBJ(value) && AS_OBJ(value)->type == type;
+}
+
+#endif//CLOX_OBJECT_H

+ 32 - 3
value.c

@@ -1,6 +1,8 @@
-#include <stdio.h>
 #include "value.h"
 #include "memory.h"
+#include <stdio.h>
+#include <string.h>
+#include "object.h"
 
 void initValueArray(ValueArray *array) {
   array->values = NULL;
@@ -22,5 +24,32 @@ void freeValueArray(ValueArray *array) {
   initValueArray(array);
 }
 void printValue(Value value) {
-  printf("%g", AS_NUMBER(value));
-};
+  switch (value.type) {
+    case VAL_BOOL:
+      printf(AS_BOOL(value) ? "true" : "false");
+      break;
+    case VAL_NIL:
+      printf("nil");
+      break;
+    case VAL_NUMBER:
+      printf("%g", AS_NUMBER(value));
+      break;
+    case VAL_OBJ:
+      printObject(value);
+      break;
+  }
+}
+bool valuesEqual(Value a, Value b) {
+  if (a.type != b.type) return false;
+  switch (a.type) {
+    case VAL_BOOL: return AS_BOOL(a) == AS_BOOL(b);
+    case VAL_NIL: return true;
+    case VAL_NUMBER: return AS_NUMBER(a) == AS_NUMBER(b);
+    case VAL_OBJ: {
+      ObjString *aString = AS_STRING(a);
+      ObjString *bString = AS_STRING(b);
+      return aString->length == bString->length && memcmp(aString->chars, bString->chars, aString->length) == 0;
+    }
+    default: return false;
+  }
+}

+ 23 - 14
value.h

@@ -13,38 +13,47 @@
 
 #include "common.h"
 
+typedef struct Obj Obj;
+typedef struct ObjString ObjString;
+
 typedef enum {
-    VAL_BOOL,
-    VAL_NIL,
-    VAL_NUMBER,
+  VAL_BOOL,
+  VAL_NIL,
+  VAL_NUMBER,
+  VAL_OBJ,/// heap-allocated types: strings, instances, functions
 } ValueType;
 
 typedef struct {
-    ValueType type;
-    union {
-        bool boolean;
-        double number;
-    } as;
+  ValueType type;
+  union {
+    bool boolean;
+    double number;
+    Obj *obj;
+  } as;
 } Value;
 
 #define IS_BOOL(value) ((value).type == VAL_BOOL)
 #define IS_NIL(value) ((value).type == VAL_NIL)
 #define IS_NUMBER(value) ((value).type == VAL_NUMBER)
+#define IS_OBJ(value) ((value).type == VAL_OBJ)
 
+#define AS_OBJ(value) ((value).as.obj)
 #define AS_BOOL(value) ((value).as.boolean)
 #define AS_NUMBER(value) ((value).as.number)
 
-#define BOOL_VAL(vale) ((Value){VAL_BOOL, {.boolean = value}})
-#define NIL_VAL(vale) ((Value){VAL_NIL, {.number = 0}})
+#define BOOL_VAL(value) ((Value){VAL_BOOL, {.boolean = value}})
+#define NIL_VAL ((Value){VAL_NIL, {.number = 0}})
 #define NUMBER_VAL(value) ((Value){VAL_NUMBER, {.number = value}})
+#define OBJ_VAL(object) ((Value){VAL_OBJ, {.obj = (Obj *) object}})
 
 /// \brief 常量池
 typedef struct {
-    int capacity;
-    int count;
-    Value *values;
+  int capacity;
+  int count;
+  Value *values;
 } ValueArray;
 
+bool valuesEqual(Value a, Value b);
 void initValueArray(ValueArray *array);
 
 void writeValueArray(ValueArray *array, Value value);
@@ -53,4 +62,4 @@ void freeValueArray(ValueArray *array);
 
 void printValue(Value value);
 
-#endif //CLOX__VALUE_H_
+#endif//CLOX__VALUE_H_

+ 140 - 88
vm.c

@@ -7,40 +7,58 @@
   * @date           : 2023/8/17
   ******************************************************************************
   */
-#include <stdarg.h>
-#include "common.h"
 #include "vm.h"
-#include "debug.h"
+#include "common.h"
 #include "compiler.h"
+#include "debug.h"
+#include "memory.h"
+#include <stdarg.h>
 
 VM vm;
 
 /// \brief 重置栈
 static void resetStack() {
-    vm.stackTop = vm.stack; // 指针指向栈底
+  vm.stackTop = vm.stack;// 指针指向栈底
 }
 
 static void runtimeError(const char *format, ...) {
-    va_list args;
-    va_start(args, format);
-    vfprintf(stderr, format, args);
-    va_end(args);
-    fputs("\n", stderr);
-
-    size_t instruction = vm.ip - vm.chunk->code - 1;
-    int line = vm.chunk->lines[instruction];
-    fprintf(stderr, "[line %d] in script\n", line);
-    resetStack();
+  va_list args;
+  va_start(args, format);
+  vfprintf(stderr, format, args);
+  va_end(args);
+  fputs("\n", stderr);
+
+  size_t instruction = vm.ip - vm.chunk->code - 1;
+  int line = vm.chunk->lines[instruction];
+  fprintf(stderr, "[line %d] in script\n", line);
+  resetStack();
 }
 
 void initVM() {
-    resetStack();
+  resetStack();
+  vm.objects = NULL;
 }
 
 static Value peek(int distance) {
-    return vm.stackTop[-1 - distance];
+  return vm.stackTop[-1 - distance];
+}
+static bool isFalse(Value value) {
+  // the  nil and false will return true
+  return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
+}
+static void concatenate() {
+  ObjString *b = AS_STRING(pop());
+  ObjString *a = AS_STRING(pop());
+
+  int length = a->length + b->length;
+  char *chars = ALLOCATE(char, length + 1);
+  memcpy(chars, a->chars, a->length);
+  memcpy(chars + a->length, b->chars, b->length);
+  chars[length] = '\0';
+
+  ObjString *result = takeString(chars, length);
+  push(OBJ_VAL(result));
 }
-
 static InterpretResult run() {
 //! <macro> reads the byte currently pointed at by ip
 //! and then advances the instruction pointer
@@ -48,63 +66,98 @@ static InterpretResult run() {
 //! reads the next byte from the bytecode
 //! treats the resulting number as an index
 #define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
-#define BINARY_OP(valueType, op) \
-   do {               \
-     if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \
-         runtimeError("Operands must be numbers."); \
-         return INTERPRET_RUNTIME_ERROR; \
-     } \
-     double b = AS_NUMBER(pop()); \
-     double a = AS_NUMBER(pop()); \
-     push(valueType(a op b)); \
-   } while(false)
-
-    for (;;) {
+#define BINARY_OP(valueType, op)                      \
+  do {                                                \
+    if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \
+      runtimeError("Operands must be numbers.");      \
+      return INTERPRET_RUNTIME_ERROR;                 \
+    }                                                 \
+    double b = AS_NUMBER(pop());                      \
+    double a = AS_NUMBER(pop());                      \
+    push(valueType(a op b));                          \
+  } while (false)
+
+  for (;;) {
 #ifdef DEBUG_TRACE_EXECUTION
-        //! <Stack tracing> start
-        printf("------------------------\n|");
-        for (Value *slot = vm.stack; slot < vm.stackTop; slot++) {
-            printf("[ ");
-            printValue(*slot);
-            printf(" ]");
-        }
-        printf("\n------------------------");
-        printf("\n");
-        //! <Stack tracing> end
-        disassembleInstruction(vm.chunk, (int) (vm.ip - vm.chunk->code));
+    //! <Stack tracing> start
+    printf("------------------------\n|");
+    for (Value *slot = vm.stack; slot < vm.stackTop; slot++) {
+      printf("[ ");
+      printValue(*slot);
+      printf(" ]");
+    }
+    printf("\n------------------------");
+    printf("\n");
+    //! <Stack tracing> end
+    disassembleInstruction(vm.chunk, (int) (vm.ip - vm.chunk->code));
 #endif
-        switch (READ_BYTE()) {
-            case OP_CONSTANT: {
-                Value constant = READ_CONSTANT();
-                push(constant);
-                break;
-            }
-            case OP_ADD:
-                BINARY_OP(NUMBER_VAL, +);
-                break;
-            case OP_SUBTRACT:
-                BINARY_OP(NUMBER_VAL, -);
-                break;
-            case OP_MULTIPLY:
-                BINARY_OP(NUMBER_VAL, *);
-                break;
-            case OP_DIVIDE:
-                BINARY_OP(NUMBER_VAL, /);
-                break;
-            case OP_NEGATE:
-                if (!IS_NUMBER(peek(0))) {
-                    runtimeError("Operand must be a number.");
-                    return INTERPRET_RUNTIME_ERROR;
-                }
-                push(NUMBER_VAL(-AS_NUMBER(pop())));
-                break;
-            case OP_RETURN: {
-                printValue(pop());
-                printf("\n");
-                return INTERPRET_OK;
-            }
+    switch (READ_BYTE()) {
+      case OP_CONSTANT: {
+        Value constant = READ_CONSTANT();
+        push(constant);
+        break;
+      }
+      case OP_NIL:
+        push(NIL_VAL);
+        break;
+      case OP_TRUE:
+        push(BOOL_VAL(true));
+        break;
+      case OP_FALSE:
+        push(BOOL_VAL(false));
+        break;
+      case OP_ADD: {
+        if (IS_STRING(peek(0)) && IS_STRING(peek(1))) {
+          // 字串拼接
+          concatenate();
+        } else if (IS_NUMBER(peek(0)) && IS_NUMBER(peek(1))) {
+          double b = AS_NUMBER(pop());
+          double a = AS_NUMBER(pop());
+          push(NUMBER_VAL(a + b));
+        } else {
+          runtimeError("Operands must be two numbers or two strings.");
+          return INTERPRET_RUNTIME_ERROR;
+        }
+        break;
+      }
+      case OP_SUBTRACT:
+        BINARY_OP(NUMBER_VAL, -);
+        break;
+      case OP_MULTIPLY:
+        BINARY_OP(NUMBER_VAL, *);
+        break;
+      case OP_DIVIDE:
+        BINARY_OP(NUMBER_VAL, /);
+        break;
+      case OP_NOT:
+        push(BOOL_VAL(isFalse(pop())));
+        break;
+      case OP_NEGATE:
+        if (!IS_NUMBER(peek(0))) {
+          runtimeError("Operand must be a number.");
+          return INTERPRET_RUNTIME_ERROR;
         }
+        push(NUMBER_VAL(-AS_NUMBER(pop())));
+        break;
+      case OP_EQUAL: {
+        Value b = pop();
+        Value a = pop();
+        push(BOOL_VAL(valuesEqual(a, b)));
+        break;
+      }
+      case OP_GREATER:
+        BINARY_OP(BOOL_VAL, >);
+        break;
+      case OP_LESS:
+        BINARY_OP(BOOL_VAL, <);
+        break;
+      case OP_RETURN: {
+        printValue(pop());
+        printf("\n");
+        return INTERPRET_OK;
+      }
     }
+  }
 
 #undef READ_BYTE
 #undef READ_CONSTANT
@@ -112,35 +165,34 @@ static InterpretResult run() {
 }
 
 void freeVM() {
-
+  freeObjects();
 }
 
 InterpretResult interpret(const char *source) {
-    Chunk chunk;
-    initChunk(&chunk);
+  Chunk chunk;
+  initChunk(&chunk);
 
-    if (!compile(source, &chunk)) {
-        freeChunk(&chunk);
-        return INTERPRET_COMPILE_ERROR;
-    }
+  if (!compile(source, &chunk)) {
+    freeChunk(&chunk);
+    return INTERPRET_COMPILE_ERROR;
+  }
 
-    vm.chunk = &chunk;
-    vm.ip = vm.chunk->code;
+  vm.chunk = &chunk;
+  vm.ip = vm.chunk->code;
 
-    InterpretResult result = run();
+  InterpretResult result = run();
 
-    freeChunk(&chunk);
-    return result;
+  freeChunk(&chunk);
+  return result;
 }
 
 void push(Value value) {
-    *vm.stackTop = value;
-    vm.stackTop++;
+  *vm.stackTop = value;
+  vm.stackTop++;
 }
 
 Value pop() {
-    vm.stackTop--;
-    //! NOTE: 出栈后,里面的值没有清除
-    return *vm.stackTop;
+  vm.stackTop--;
+  //! NOTE: 出栈后,里面的值没有清除
+  return *vm.stackTop;
 }
-

+ 6 - 3
vm.h

@@ -14,13 +14,14 @@
 #include "chunk.h"
 #include "value.h"
 
-#define  STACK_MAX 256
+#define STACK_MAX 256
 
 typedef struct {
   Chunk *chunk;
   uint8_t *ip;
   Value stack[STACK_MAX];
-  Value *stackTop;  // 栈指针
+  Value *stackTop;// 栈指针
+  Obj *objects;   // 管理分配的 heap 内存
 } VM;
 typedef enum {
   INTERPRET_OK,
@@ -28,6 +29,8 @@ typedef enum {
   INTERPRET_RUNTIME_ERROR
 } InterpretResult;
 
+extern VM  vm;
+
 void initVM();
 void freeVM();
 /// \brief interpret 执行指令
@@ -37,4 +40,4 @@ InterpretResult interpret(const char *source);
 void push(Value value);
 Value pop();
 
-#endif //CLOX__VM_H_
+#endif//CLOX__VM_H_