Kajetan Johannes Hammerle 6 дней назад
Родитель
Сommit
f24f4b0062
6 измененных файлов с 146 добавлено и 1 удалено
  1. 79 0
      src/Compiler.c
  2. 1 1
      src/Main.c
  3. 12 0
      src/Tokenizer.c
  4. 4 0
      src/Tokenizer.h
  5. 24 0
      test/For.basic
  6. 26 0
      test/For.basic_result

+ 79 - 0
src/Compiler.c

@@ -36,6 +36,14 @@ struct Return {
     bool hasArgument;
 };
 
+typedef struct BreakContinue BreakContinue;
+
+struct BreakContinue {
+    BreakContinue* next;
+    size_t address;
+    bool stop;
+};
+
 typedef struct {
     Tokenizer* tokenizer;
     Code* code;
@@ -45,7 +53,9 @@ typedef struct {
     Variable* variables;
     Function* functions;
     Return* returns;
+    BreakContinue* loopJumps;
     bool inFunction;
+    i32 inLoop;
 } Context;
 
 #define THROW_ERROR(format, ...)                                   \
@@ -191,6 +201,17 @@ static void addReturn(Context* c, i32 address, bool hasArgument) {
     c->returns = r;
 }
 
+static void addLoopJump(Context* c, size_t address, bool stop) {
+    BreakContinue* r = memoryAllocate(sizeof(BreakContinue));
+    if(r == nullptr) {
+        THROW_ERROR("Too less memory for break/continue");
+    }
+    r->next = c->loopJumps;
+    r->stop = stop;
+    r->address = address;
+    c->loopJumps = r;
+}
+
 #define CLEAN_LIST(Type, field)   \
     do {                          \
         Type* v = c->field;       \
@@ -210,6 +231,7 @@ static void cleanContext(Context* c) {
     CLEAN_LIST(Variable, variables);
     CLEAN_LIST(Function, functions);
     cleanReturns(c);
+    CLEAN_LIST(BreakContinue, loopJumps);
 }
 
 [[noreturn]] static void unexpectedToken(Context* c, Token token, int line) {
@@ -379,6 +401,47 @@ static void compileSetVariable(Context* c, const char* name, bool forceGlobal) {
     codePushInstructionI32(c, SET_VARIABLE, addVariable(c, name, forceGlobal));
 }
 
+static void compileFor(Context* c) {
+    c->inLoop++;
+    Token t = consumeToken(c, TT_LITERAL);
+    compileSetVariable(c, t.stringValue, false);
+    consumeToken(c, TT_TO);
+    size_t checkStart = codeGetWritePosition(c);
+    compileExpression(c);
+    consumeNewline(c);
+    compileReadVariable(c, t, false);
+    codePushInstruction(c, GREATER_OR_EQUAL);
+    size_t end = codePushInstructionI32(c, JUMP_ON_0, 0);
+
+    BreakContinue* currentJumps = c->loopJumps;
+    while(!peekToken(c, TT_END)) {
+        compileLine(c, tokenizerNext(c->tokenizer));
+    }
+    consumeToken(c, TT_END);
+    consumeNewline(c);
+
+    i32 continueStart = (i32)codeGetWritePosition(c);
+    compileReadVariable(c, t, false);
+    codePushInstructionI32(c, PUSH_INT32, 1);
+    codePushInstruction(c, ADD);
+    codePushInstructionI32(
+        c, SET_VARIABLE, addVariable(c, t.stringValue, false));
+
+    codePushInstructionI32(c, JUMP, (i32)checkStart);
+    i32 endStart = (i32)codeGetWritePosition(c);
+    codeRewriteI32(c, end, endStart);
+
+    while(c->loopJumps != currentJumps) {
+        BreakContinue* next = c->loopJumps->next;
+        codeRewriteI32(
+            c, c->loopJumps->address,
+            c->loopJumps->stop ? endStart : continueStart);
+        memoryFree(c->loopJumps);
+        c->loopJumps = next;
+    }
+    c->inLoop--;
+}
+
 static i32 compileFunctionArguments(Context* c) {
     consumeToken(c, TT_OPEN_ROUND_BRACKET);
     i32 vars = 0;
@@ -500,6 +563,22 @@ static void compileLine(Context* c, Token token) {
         c->line++;
     } else if(token.type == TT_IF) {
         compileIf(c);
+    } else if(token.type == TT_BREAK) {
+        if(c->inLoop <= 0) {
+            THROW_ERROR("break without loop");
+        }
+        size_t pos = codePushInstructionI32(c, JUMP, 9999);
+        addLoopJump(c, pos, true);
+        consumeNewline(c);
+    } else if(token.type == TT_CONTINUE) {
+        if(c->inLoop <= 0) {
+            THROW_ERROR("continue without loop");
+        }
+        size_t pos = codePushInstructionI32(c, JUMP, 9999);
+        addLoopJump(c, pos, false);
+        consumeNewline(c);
+    } else if(token.type == TT_FOR) {
+        compileFor(c);
     } else if(token.type == TT_FUNCTION) {
         compileFunction(c);
     } else if(token.type == TT_RETURN) {

+ 1 - 1
src/Main.c

@@ -31,7 +31,7 @@ int main(int argCount, const char** args) {
         puts(e.text);
     } else {
         // codeDump(&code);
-        //   return 0;
+        // return 0;
         // memoryDump();
         codeRun(&code);
         if(codeHasRunError(&code)) {

+ 12 - 0
src/Tokenizer.c

@@ -75,6 +75,14 @@ static const char* tokenizerAddLiteral(TState* t, const char* s) {
         tAddToken(t, TT_END);
     } else if(strcmp(buffer, "return") == 0) {
         tAddToken(t, TT_RETURN);
+    } else if(strcmp(buffer, "for") == 0) {
+        tAddToken(t, TT_FOR);
+    } else if(strcmp(buffer, "to") == 0) {
+        tAddToken(t, TT_TO);
+    } else if(strcmp(buffer, "break") == 0) {
+        tAddToken(t, TT_BREAK);
+    } else if(strcmp(buffer, "continue") == 0) {
+        tAddToken(t, TT_CONTINUE);
     } else {
         tAddToken(t, TT_LITERAL);
         if(bufferWriteString(&t->tokenizer->buffer, buffer)) {
@@ -295,6 +303,10 @@ void tokenizerPrintToken(const Token* token, char* buffer, size_t n) {
         TOKEN_PRINTER(TT_FUNCTION, "Function");
         TOKEN_PRINTER(TT_END, "End");
         TOKEN_PRINTER(TT_RETURN, "Return");
+        TOKEN_PRINTER(TT_FOR, "For");
+        TOKEN_PRINTER(TT_TO, "To");
+        TOKEN_PRINTER(TT_BREAK, "Break");
+        TOKEN_PRINTER(TT_CONTINUE, "Continue");
         TOKEN_PRINTER(TT_OPEN_ROUND_BRACKET, "(");
         TOKEN_PRINTER(TT_CLOSE_ROUND_BRACKET, ")");
         TOKEN_PRINTER(TT_NEWLINE, "Newline");

+ 4 - 0
src/Tokenizer.h

@@ -28,6 +28,10 @@ typedef enum : u8 {
     TT_FUNCTION,
     TT_END,
     TT_RETURN,
+    TT_FOR,
+    TT_TO,
+    TT_BREAK,
+    TT_CONTINUE,
     TT_OPEN_ROUND_BRACKET,
     TT_CLOSE_ROUND_BRACKET,
     TT_NEWLINE,

+ 24 - 0
test/For.basic

@@ -0,0 +1,24 @@
+for i = 0 to 5
+    print(i)
+end
+
+for x = 0 to 2
+    for y = 0 to 3
+        print(x, " ", y)
+    end
+end
+
+for x = 0 to 10
+    for y = 0 to 5
+        if y == 1
+            continue
+        end
+        if y == 3
+            continue
+        end
+        print(x, " ", y)
+    end
+    if x == 1
+        break
+    end
+end

+ 26 - 0
test/For.basic_result

@@ -0,0 +1,26 @@
+0
+1
+2
+3
+4
+5
+0 0
+0 1
+0 2
+0 3
+1 0
+1 1
+1 2
+1 3
+2 0
+2 1
+2 2
+2 3
+0 0
+0 2
+0 4
+0 5
+1 0
+1 2
+1 4
+1 5