Преглед на файлове

add condition statement

runningwater преди 2 години
родител
ревизия
786f9bc1f1
променени са 5 файла, в които са добавени 140 реда и са изтрити 4 реда
  1. 7 0
      .idea/runConfigurations/assign__clox.xml
  2. 2 1
      chunk.h
  3. 125 3
      compiler.c
  4. 1 0
      debug.c
  5. 5 0
      vm.c

+ 7 - 0
.idea/runConfigurations/assign__clox.xml

@@ -0,0 +1,7 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="assign- clox" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="$ProjectFileDir$/test/assign.lox" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="clox" TARGET_NAME="clox" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="clox" RUN_TARGET_NAME="clox">
+    <method v="2">
+      <option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
+    </method>
+  </configuration>
+</component>

+ 2 - 1
chunk.h

@@ -37,7 +37,8 @@ typedef enum {
   OP_DIVIDE,  /// \brief /
   OP_PRINT,
   OP_JUMP_IF_FALSE,/// <OP_JUMP_IF_FALSE + +>
-  OP_JUMP,         /// <OP_JUMP + +>
+  OP_JUMP,         /// <OP_JUMP + +> 往后跳
+  OP_LOOP,         /// <OP_LOOP ++>  往前跳
   OP_RETURN,       ///<OP_RETURN>
 } OpCode;
 

+ 125 - 3
compiler.c

@@ -129,6 +129,15 @@ static void emitBytes(uint8_t byte1, uint8_t byte2) {
   emitByte(byte1);
   emitByte(byte2);
 }
+static void emitLoop(int loopStart) {
+  emitByte(OP_LOOP);
+
+  int offset = currentChunk()->count - loopStart + 2;
+  if (offset > UINT16_MAX) error("Loop body too large.");
+
+  emitByte((offset >> 8) & 0xFF);
+  emitByte(offset & 0xFF);
+}
 static int emitJump(uint8_t instruction) {
   emitByte(instruction);
   emitByte(0xff);// 占位字节,后面填入需要跳转的 offset
@@ -178,6 +187,71 @@ static void patchJump(int offset) {
   currentChunk()->code[offset] = (jump >> 8) & 0xFF;
   currentChunk()->code[offset + 1] = jump & 0xFF;
 }
+static void forStatement() {
+  /**
+   * *********************************************************
+   *  initializer clause
+   * L4: condition expression
+   *  OP_JUMP_IF_FALSE L1
+   *  OP_POP
+   *  OP_JUMP L3
+   * L2: increment expression
+   *  OP_POP
+   * OP_LOOP L4
+   * L3: body statement
+   *  OP_LOOP L2
+   *  L1: OP_POP
+   *  continues...
+   * *********************************************************
+   */
+  beginScope();
+  // for(I;II;III)
+  consume(TOKEN_LEFT_PAREN, "Expect '(' after 'for'.");
+
+  ///! I.<<Initializer Part>>
+  if (match(TOKEN_SEMICOLON)) {
+    // No initializer.
+  } else if (match(TOKEN_VAR)) {
+    varDeclaration();
+  } else {
+    expressionStatement();// ; op_pop
+  }
+
+  ///! II.<<Condition Part>>
+  int loopStart = currentChunk()->count;
+  int exitJump = -1;
+  if (!match(TOKEN_SEMICOLON)) {
+    expression();
+    consume(TOKEN_SEMICOLON, "Expect ';' after loop conditon. ");
+
+    // Jump out of the loop if the condition is false.
+    exitJump = emitJump(OP_JUMP_IF_FALSE);
+    emitByte(OP_POP);
+  }
+
+  ///! III.<<Increment Clause>>
+  if (!match(TOKEN_RIGHT_PAREN)) {
+    int bodyJump = emitJump(OP_JUMP);
+    int incrementStart = currentChunk()->count;
+    expression();
+    emitByte(OP_POP);
+    consume(TOKEN_RIGHT_PAREN, "Expect ')' after for clauses.");
+
+    emitLoop(loopStart);
+    loopStart = incrementStart;
+    patchJump(bodyJump);
+  }
+
+  ///! <<Body Part>>
+  statement();
+  emitLoop(loopStart);
+
+  if (exitJump != -1) {
+    patchJump(exitJump);
+    emitByte(OP_POP);// Condition.
+  }
+  endScope();
+}
 static void ifStatement() {
   consume(TOKEN_LEFT_PAREN, "Expect '(' after 'if'.");
   expression();
@@ -195,6 +269,21 @@ static void ifStatement() {
   if (match(TOKEN_ELSE)) statement();// else branch statement
   patchJump(elseJump);
 }
+static void whileStatement() {
+  int loopStart = currentChunk()->count;
+  consume(TOKEN_LEFT_PAREN, "Expect '(' after 'while'.");
+  expression();// while condition.
+  consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition.");
+
+  int exitJump = emitJump(OP_JUMP_IF_FALSE);
+  emitByte(OP_POP);
+
+  statement();// while body.
+  emitLoop(loopStart);
+
+  patchJump(exitJump);
+  emitByte(OP_POP);
+}
 static void printStatement() {
   expression();
   consume(TOKEN_SEMICOLON, "Expect ';' after value.");
@@ -250,8 +339,12 @@ static void block() {
 static void statement() {
   if (match(TOKEN_PRINT)) {
     printStatement();
+  } else if (match(TOKEN_FOR)) {
+    forStatement();
   } else if (match(TOKEN_IF)) {
     ifStatement();
+  } else if (match(TOKEN_WHILE)) {
+    whileStatement();
   } else if (match(TOKEN_LEFT_BRACE)) {
     ///! block → "{" declaration* "}" ;
     beginScope();
@@ -303,6 +396,36 @@ static void string(__attribute__((unused)) bool canAssign) {
   emitConstant(OBJ_VAL(copyString(parser.previous.start + 1,
                                   parser.previous.length - 2)));
 }
+static void and_(__attribute__((unused)) bool canAssign) {
+  /// left operand expression
+  /// OP_JUMP_IF_FALSE b
+  /// OP_POP
+  /// right operand expression
+  /// b: continues...
+  int endJump = emitJump(OP_JUMP_IF_FALSE);
+
+  emitByte(OP_POP);
+
+  parsePrecedence(PREC_AND);// right operand expression
+
+  patchJump(endJump);
+}
+static void or_(__attribute__((unused)) bool canAssign) {
+  /// left operand expression
+  /// OP_JUMP_IF_FALSE b1
+  /// OP_JUMP b2
+  /// b1: OP_POP
+  /// right operand expression
+  /// b2: continue
+  int elseJump = emitJump(OP_JUMP_IF_FALSE);
+  int endJump = emitJump(OP_JUMP);
+
+  patchJump(elseJump);
+  emitByte(OP_POP);
+
+  parsePrecedence(PREC_OR);// right operand expression
+  patchJump(endJump);
+}
 static int resolveLocal(Compiler *compile, Token *name) {
   for (int i = compile->localCount - 1; i >= 0; i--) {
     Local *local = &compile->locals[i];
@@ -433,7 +556,7 @@ ParseRule rules[] = {
     [TOKEN_IDENTIFIER] = {variable, NULL, PREC_NONE},
     [TOKEN_STRING] = {string, NULL, PREC_NONE},
     [TOKEN_NUMBER] = {number, NULL, PREC_NONE},
-    [TOKEN_AND] = {NULL, NULL, PREC_NONE},
+    [TOKEN_AND] = {NULL, and_, PREC_AND},
     [TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
     [TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
     [TOKEN_FALSE] = {literal, NULL, PREC_NONE},
@@ -441,7 +564,7 @@ ParseRule rules[] = {
     [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_OR] = {NULL, or_, PREC_OR},
     [TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
     [TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
     [TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
@@ -532,7 +655,6 @@ static void defineVariable(uint8_t global) {
     // 本地变量,直接退出
     return;
   }
-
   emitBytes(OP_DEFINE_GLOBAL, global);
 }
 static ParseRule *getRule(TokenType type) {

+ 1 - 0
debug.c

@@ -54,6 +54,7 @@ int disassembleInstruction(Chunk *chunk, int offset) {
     case OP_LESS: return simpleInstruction("OP_LESS", 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_JUMP_IF_FALSE: return jumpInstruction("OP_JUMP_IF_FALSE", 1, chunk, offset);
     case OP_RETURN: return simpleInstruction("OP_RETURN", offset);
     default:

+ 5 - 0
vm.c

@@ -212,6 +212,11 @@ static InterpretResult run() {
         vm.ip += offset;
         break;
       }
+      case OP_LOOP: {
+        uint16_t offset = READ_SHORT();
+        vm.ip -= offset;
+        break;
+      }
       case OP_RETURN: {
         // Exit interpreter.
         return INTERPRET_OK;