Browse Source

add global variables

runningwater 2 năm trước cách đây
mục cha
commit
dc99c8d6d8
6 tập tin đã thay đổi với 222 bổ sung17 xóa
  1. 8 3
      chunk.h
  2. 145 12
      compiler.c
  3. 5 0
      debug.c
  4. 21 0
      test/assign.lox
  5. 42 1
      vm.c
  6. 1 1
      vm.h

+ 8 - 3
chunk.h

@@ -20,8 +20,12 @@ typedef enum {
   OP_NIL,
   OP_TRUE,
   OP_FALSE,
-  OP_NOT,   /// \brief print !true; // "false"
-  OP_NEGATE,/// \brief prefix -
+  OP_POP,
+  OP_DEFINE_GLOBAL,
+  OP_GET_GLOBAL,
+  OP_SET_GLOBAL,/// \brief setter
+  OP_NOT,       /// \brief print !true; // "false"
+  OP_NEGATE,    /// \brief prefix -
   OP_EQUAL,
   OP_GREATER,
   OP_LESS,
@@ -29,7 +33,8 @@ typedef enum {
   OP_SUBTRACT,/// \brief -
   OP_MULTIPLY,/// \brief *
   OP_DIVIDE,  /// \brief /
-  OP_RETURN,  ///<OP_RETURN>
+  OP_PRINT,
+  OP_RETURN,///<OP_RETURN>
 } OpCode;
 
 //============================================================================

+ 145 - 12
compiler.c

@@ -35,7 +35,7 @@ typedef enum {
   PREC_PRIMARY
 } Precedence;
 
-typedef void (*ParseFn)();
+typedef void (*ParseFn)(bool canAssign);
 
 typedef struct {
   ParseFn prefix;
@@ -47,7 +47,12 @@ Parser parser;
 Chunk *compilingChunk;
 
 static void parsePrecedence(Precedence);
+static uint8_t parseVariable(const char *);
+static void defineVariable(uint8_t);
+static uint8_t identifierConstant(Token *name);
 static ParseRule *getRule(TokenType);
+static void statement();
+static void declaration();
 
 static Chunk *currentChunk() {
   return compilingChunk;
@@ -93,6 +98,14 @@ void consume(TokenType type, const char *message) {
 
   errorAtCurrent(message);
 }
+static bool check(TokenType type) {
+  return parser.current.type == type;
+}
+static bool match(TokenType type) {
+  if (!check(type)) return false;
+  advance();
+  return true;
+}
 static void emitByte(uint8_t byte) {
   writeChunk(currentChunk(), byte, parser.previous.line);
 }
@@ -114,6 +127,77 @@ static uint8_t makeConstant(Value value) {
 static void expression() {
   parsePrecedence(PREC_ASSIGNMENT);
 }
+/// for example: var a; var b = 10;
+static void varDeclaration() {
+  uint8_t global = parseVariable("Expect variable name.");
+
+  if (match(TOKEN_EQUAL)) {
+    expression();
+  } else {
+    emitByte(OP_NIL);
+  }
+
+  consume(TOKEN_SEMICOLON, "Expect ';' after variable declaration.");
+
+  defineVariable(global);
+}
+static void expressionStatement() {
+  expression();
+  consume(TOKEN_SEMICOLON, "Expect ';' after expression.");
+  emitByte(OP_POP);
+}
+static void printStatement() {
+  expression();
+  consume(TOKEN_SEMICOLON, "Expect ';' after value.");
+  emitByte(OP_PRINT);
+}
+static void synchronize() {
+  parser.panicMode = false;
+  while (parser.current.type != TOKEN_EOF) {
+    if (parser.previous.type == TOKEN_SEMICOLON) return;
+    switch (parser.current.type) {
+      case TOKEN_CLASS:
+      case TOKEN_FUN:
+      case TOKEN_VAR:
+      case TOKEN_FOR:
+      case TOKEN_IF:
+      case TOKEN_WHILE:
+      case TOKEN_PRINT:
+      case TOKEN_RETURN:
+        return;
+
+      default:;// Do nothing.
+    }
+    advance();
+  }
+}
+///declaration    → classDecl
+///               | funDecl
+///               | varDecl
+///               | statement ;
+static void declaration() {
+  if (match(TOKEN_VAR)) {
+    varDeclaration();
+  } else {
+    statement();
+  }
+
+  if (parser.panicMode) synchronize();
+}
+///statement      → exprStmt
+///               | forStmt
+///               | ifStmt
+///               | printStmt
+///               | returnStmt
+///               | whileStmt
+///               | block ;
+static void statement() {
+  if (match(TOKEN_PRINT)) {
+    printStatement();
+  } else {
+    expressionStatement();
+  }
+}
 static void emitConstant(Value value) {
   emitBytes(OP_CONSTANT, makeConstant(value));
 }
@@ -125,22 +209,36 @@ static void endCompiler() {
   }
 #endif
 }
-static void grouping() {
+static void grouping(__attribute__((unused)) bool canAssign) {
   expression();
   consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
 }
 /// \brief parse number token
 /// Number literals: 123
-static void number() {
+static void number(__attribute__((unused)) bool canAssign) {
   double value = strtod(parser.previous.start, NULL);
   emitConstant(NUMBER_VAL(value));
 }
-static void string() {
+static void string(__attribute__((unused)) bool canAssign) {
   emitConstant(OBJ_VAL(copyString(parser.previous.start + 1,
                                   parser.previous.length - 2)));
 }
+static void namedVariable(Token name, bool canAssign) {
+  uint8_t arg = identifierConstant(&name);
+
+  if (canAssign && match(TOKEN_EQUAL)) {
+    // 如 menu.brunch(sunday).beverage = "mimosa";
+    expression();
+    emitBytes(OP_SET_GLOBAL, arg);
+  } else {
+    emitBytes(OP_GET_GLOBAL, arg);
+  }
+}
+static void variable(bool canAssign) {
+  namedVariable(parser.previous, canAssign);
+}
 /// Unary negation: -123
-static void unary() {
+static void unary(__attribute__((unused)) bool canAssign) {
   TokenType operatorType = parser.previous.type;
 
   // Compile the operand.
@@ -158,7 +256,7 @@ static void unary() {
   }
 }
 /// \brief infix parser
-static void binary() {
+static void binary(__attribute__((unused)) bool canAssign) {
   TokenType operatorType = parser.previous.type;
   ParseRule *rule = getRule(operatorType);
   parsePrecedence((Precedence) rule->precedence + 1);
@@ -197,7 +295,7 @@ static void binary() {
     default: return;// Unreachable.
   }
 }
-static void literal() {
+static void literal(__attribute__((unused)) bool canAssign) {
   switch (parser.previous.type) {
     case TOKEN_FALSE:
       emitByte(OP_FALSE);
@@ -231,7 +329,7 @@ ParseRule rules[] = {
     [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_IDENTIFIER] = {variable, NULL, PREC_NONE},
     [TOKEN_STRING] = {string, NULL, PREC_NONE},
     [TOKEN_NUMBER] = {number, NULL, PREC_NONE},
     [TOKEN_AND] = {NULL, NULL, PREC_NONE},
@@ -264,17 +362,49 @@ static void parsePrecedence(Precedence precedence) {
     return;
   }
 
-  prefixRule();///! 执行具体函数
+  bool canAssign = precedence <= PREC_ASSIGNMENT;
+  prefixRule(canAssign);///! 执行具体函数
 
   while (precedence <= getRule(parser.current.type)->precedence) {
     advance();
     ParseFn infixRule = getRule(parser.previous.type)->infix;
-    infixRule();///! 执行具体函数
+    infixRule(canAssign);///! 执行具体函数
+  }
+
+  if (canAssign && match(TOKEN_EQUAL)) {
+    error("Invalid assignment target.");
   }
 }
+static uint8_t identifierConstant(Token *name) {
+  return makeConstant(OBJ_VAL(copyString(name->start, name->length)));
+}
+static uint8_t parseVariable(const char *errorMessage) {
+  consume(TOKEN_IDENTIFIER, errorMessage);
+  return identifierConstant(&parser.previous);
+}
+static void defineVariable(uint8_t global) {
+  emitBytes(OP_DEFINE_GLOBAL, global);
+}
 static ParseRule *getRule(TokenType type) {
   return &rules[type];
 }
+
+/**
+  ******************************************************************************
+  * statement      → exprStmt
+                      | forStmt
+                      | ifStmt
+                      | printStmt
+                      | returnStmt
+                      | whileStmt
+                      | block ;
+
+    declaration    → classDecl
+                   | funDecl
+                   | varDecl
+                   | statement ;
+  ******************************************************************************
+  */
 bool compile(const char *source, Chunk *chunk) {
   initScanner(source);
   compilingChunk = chunk;
@@ -283,8 +413,11 @@ bool compile(const char *source, Chunk *chunk) {
   parser.panicMode = false;
 
   advance();
-  expression();
-  consume(TOKEN_EOF, "Expect end of expression");
+
+  while (!match(TOKEN_EOF)) {
+    declaration();
+  }
+
   endCompiler();//! return opcode
   return !parser.hadError;
 }

+ 5 - 0
debug.c

@@ -31,6 +31,10 @@ int disassembleInstruction(Chunk *chunk, int 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_POP: return simpleInstruction("OP_POP", 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_ADD: return simpleInstruction("OP_ADD", offset);
     case OP_SUBTRACT: return simpleInstruction("OP_SUBTRACT", offset);
     case OP_MULTIPLY: return simpleInstruction("OP_MULTIPLY", offset);
@@ -40,6 +44,7 @@ int disassembleInstruction(Chunk *chunk, int 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_PRINT: return simpleInstruction("OP_PRINT", offset);
     case OP_RETURN: return simpleInstruction("OP_RETURN", offset);
     default:
       printf("Unknown opcode %d\n", instruction);

+ 21 - 0
test/assign.lox

@@ -0,0 +1,21 @@
+// == code STARTING ==
+// 0000    1 OP_CONSTANT         1 'beignets'
+// 0002    | OP_DEFINE_GLOBAL    0 'breakfast'
+// 0004    2 OP_CONSTANT         3 'cafe au lait'
+// 0006    | OP_DEFINE_GLOBAL    2 'beverage'
+// 0008    3 OP_CONSTANT         5 'beignets with '
+// 0010    | OP_GET_GLOBAL       6 'beverage'
+// 0012    | OP_ADD
+// 0013    | OP_SET_GLOBAL       4 'breakfast'
+// 0015    | OP_POP
+// 0016    5 OP_GET_GLOBAL       7 'breakfast'
+// 0018    | OP_PRINT
+// 0019    | OP_RETURN
+// == code END ==
+
+
+var breakfast = "beignets";
+var beverage = "cafe au lait";
+breakfast = "beignets with " + beverage;
+
+print breakfast;

+ 42 - 1
vm.c

@@ -37,6 +37,7 @@ static void runtimeError(const char *format, ...) {
 void initVM() {
   resetStack();
   vm.objects = NULL;
+  initTable(&vm.globals);
   initTable(&vm.strings);
 }
 
@@ -67,6 +68,7 @@ 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 READ_STRING() AS_STRING(READ_CONSTANT())
 #define BINARY_OP(valueType, op)                      \
   do {                                                \
     if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \
@@ -78,6 +80,9 @@ 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
@@ -107,6 +112,34 @@ static InterpretResult run() {
       case OP_FALSE:
         push(BOOL_VAL(false));
         break;
+      case OP_POP:
+        pop();
+        break;
+      case OP_DEFINE_GLOBAL: {
+        ObjString *name = READ_STRING();
+        tableSet(&vm.globals, name, peek(0));
+        pop();
+        break;
+      }
+      case OP_GET_GLOBAL: {
+        ObjString *name = READ_STRING();
+        Value value;
+        if (!tableGet(&vm.globals, name, &value)) {
+          runtimeError("Undefined variable '%s'.", name->chars);
+          return INTERPRET_RUNTIME_ERROR;
+        }
+        push(value);
+        break;
+      }
+      case OP_SET_GLOBAL: {
+        ObjString *name = READ_STRING();
+        if (tableSet(&vm.globals, name, peek(0))) {
+          tableDelete(&vm.globals, name);
+          runtimeError("Undefined variable '%s'.", name->chars);
+          return INTERPRET_RUNTIME_ERROR;
+        }
+        break;
+      }
       case OP_ADD: {
         if (IS_STRING(peek(0)) && IS_STRING(peek(1))) {
           // 字串拼接
@@ -152,20 +185,28 @@ static InterpretResult run() {
       case OP_LESS:
         BINARY_OP(BOOL_VAL, <);
         break;
-      case OP_RETURN: {
+      case OP_PRINT:
         printValue(pop());
         printf("\n");
+        break;
+      case OP_RETURN: {
+        // Exit interpreter.
         return INTERPRET_OK;
       }
     }
+#ifdef DEBUG_TRACE_EXECUTION
+    printf("!!! <Stack tracing> end\n");
+#endif
   }
 
 #undef READ_BYTE
 #undef READ_CONSTANT
+#undef READ_STRING
 #undef BINARY_OP
 }
 
 void freeVM() {
+  freeTable(&vm.globals);
   freeTable(&vm.strings);
   freeObjects();
 }

+ 1 - 1
vm.h

@@ -13,7 +13,6 @@
 
 #include "chunk.h"
 #include "table.h"
-#include "value.h"
 
 #define STACK_MAX 256
 
@@ -22,6 +21,7 @@ typedef struct {
   uint8_t *ip;
   Value stack[STACK_MAX];
   Value *stackTop;// 栈指针
+  Table globals;
   Table strings;  //
   Obj *objects;   // 管理分配的 heap 内存
 } VM;