Browse Source

Support functions without arguments

Kajetan Johannes Hammerle 1 week ago
parent
commit
b2444aaa97
7 changed files with 193 additions and 14 deletions
  1. 35 0
      src/Code.c
  2. 3 0
      src/Code.h
  3. 106 1
      src/Compiler.c
  4. 18 12
      src/Tokenizer.c
  5. 5 1
      src/Tokenizer.h
  6. 16 0
      test/Function.basic
  7. 10 0
      test/Function.basic_result

+ 35 - 0
src/Code.c

@@ -48,6 +48,10 @@ static void codeSetPosition(Code* c, size_t pos) {
     c->code.readIndex = pos;
 }
 
+static i32 codeGetPosition(Code* c) {
+    return (i32)c->code.readIndex;
+}
+
 static bool iPushValue(Code* c, Value v) {
     while(c->stackIndex >= c->maxStackSize) {
         size_t newSize = c->maxStackSize <= 0 ? 16 : (c->maxStackSize * 5) / 4;
@@ -113,6 +117,16 @@ static bool iPrintNewline() {
     return false;
 }
 
+static bool iJump(Code* c) {
+    i32 jumpPos = 0;
+    if(codeReadI32(c, &jumpPos)) {
+        SET_ERROR("Jump without position");
+        return true;
+    }
+    codeSetPosition(c, (size_t)jumpPos);
+    return false;
+}
+
 static bool iJumpIf(Code* c) {
     POP_VALUE(a);
     i32 jumpPos = 0;
@@ -126,6 +140,24 @@ static bool iJumpIf(Code* c) {
     return false;
 }
 
+static bool iJumpSub(Code* c) {
+    i32 jumpPos = 0;
+    if(codeReadI32(c, &jumpPos)) {
+        SET_ERROR("JumpSub without position");
+        return true;
+    } else if(iPushValue(c, INT_VALUE(codeGetPosition(c)))) {
+        return true;
+    }
+    codeSetPosition(c, (size_t)jumpPos);
+    return false;
+}
+
+static bool iReturn(Code* c) {
+    POP_VALUE(a);
+    codeSetPosition(c, (size_t)a.data);
+    return false;
+}
+
 static bool iReadVariable(Code* c) {
     i32 address = 0;
     if(codeReadI32(c, &address)) {
@@ -175,7 +207,10 @@ static bool execute(Code* c, Instruction command) {
         case PUSH_INT64: return iPushInt(c);
         case PRINT: return iPrint(c);
         case PRINT_NEWLINE: return iPrintNewline();
+        case JUMP: return iJump(c);
         case JUMP_ON_0: return iJumpIf(c);
+        case JUMP_SUB: return iJumpSub(c);
+        case RETURN: return iReturn(c);
         case READ_VARIABLE: return iReadVariable(c);
         case SET_VARIABLE: return iSetVariable(c);
         case PUSH_STACK_VARIABLES: return iPushStackVariables(c);

+ 3 - 0
src/Code.h

@@ -23,7 +23,10 @@ typedef enum : u8 {
     PUSH_INT64,
     PRINT,
     PRINT_NEWLINE,
+    JUMP,
     JUMP_ON_0,
+    JUMP_SUB,
+    RETURN,
     READ_VARIABLE,
     SET_VARIABLE,
     PUSH_STACK_VARIABLES,

+ 106 - 1
src/Compiler.c

@@ -16,6 +16,15 @@ struct Variable {
     char name[];
 };
 
+typedef struct Function Function;
+
+struct Function {
+    Function* next;
+    i32 address;
+    i32 valueAddress;
+    char name[];
+};
+
 typedef struct {
     Tokenizer* tokenizer;
     Code* code;
@@ -23,6 +32,8 @@ typedef struct {
     int line;
     jmp_buf jump;
     Variable* variables;
+    Function* functions;
+    bool inFunction;
 } Context;
 
 #define THROW_ERROR(format, ...)                                   \
@@ -82,6 +93,51 @@ static i32 addVariable(Context* c, const char* name) {
     return v->index;
 }
 
+static void addRawFunction(
+    Context* c, const char* name, i32 address, i32 valueAddress) {
+    size_t l = strlen(name) + 1;
+    Function* f = memoryAllocate(sizeof(Function) + l);
+    if(f == nullptr) {
+        THROW_ERROR("Too less memory for functions");
+    }
+    f->next = c->functions;
+    f->address = address;
+    f->valueAddress = valueAddress;
+    memcpy(f->name, name, l);
+    c->functions = f;
+}
+
+static void addFunction(Context* c, const char* name, i32 address) {
+    for(Function* f = c->functions; f != nullptr; f = f->next) {
+        if(strcmp(f->name, name) != 0) {
+            continue;
+        } else if(f->address >= 0) {
+            THROW_ERROR("Function '%s' registered again", name);
+        }
+        f->address = address;
+        codeRewriteI32(c, (size_t)f->valueAddress, address);
+    }
+    addRawFunction(c, name, address, -1);
+}
+
+static i32 addCallFunction(Context* c, const char* name, i32 address) {
+    for(Function* f = c->functions; f != nullptr; f = f->next) {
+        if(strcmp(f->name, name) == 0 && f->address >= 0) {
+            return f->address;
+        }
+    }
+    addRawFunction(c, name, -1, address);
+    return -1;
+}
+
+static void checkForInvalidFunctions(Context* c) {
+    for(Function* f = c->functions; f != nullptr; f = f->next) {
+        if(f->address < 0) {
+            THROW_ERROR("Unmapped function call '%s'", f->name);
+        }
+    }
+}
+
 static void cleanContext(Context* c) {
     Variable* v = c->variables;
     while(v != nullptr) {
@@ -90,6 +146,14 @@ static void cleanContext(Context* c) {
         v = next;
     }
     c->variables = nullptr;
+
+    Function* f = c->functions;
+    while(f != nullptr) {
+        Function* next = f->next;
+        memoryFree(f);
+        f = next;
+    }
+    c->functions = nullptr;
 }
 
 [[noreturn]] static void unexpectedToken(Context* c, Token token) {
@@ -170,6 +234,41 @@ static void compileSetVariable(Context* c, const char* name) {
     codePushI32(c, index);
 }
 
+static void compileFunction(Context* c) {
+    if(c->inFunction) {
+        THROW_ERROR("Functions in functions are not allowed");
+    }
+    c->inFunction = true;
+    codePushInstruction(c, JUMP);
+    size_t pos = codeGetWritePosition(c);
+    codePushI32(c, 0);
+
+    Token t = consumeToken(c, LITERAL);
+    addFunction(c, t.stringValue, (i32)codeGetWritePosition(c));
+    consumeToken(c, OPEN_ROUND_BRACKET);
+    consumeToken(c, CLOSE_ROUND_BRACKET);
+    while(true) {
+        if(tokenizerPeek(c->tokenizer).type == END) {
+            break;
+        }
+        compileLine(c, tokenizerNext(c->tokenizer));
+    }
+    consumeToken(c, END);
+    codePushInstruction(c, RETURN);
+
+    codeRewriteI32(c, pos, (i32)codeGetWritePosition(c));
+    c->inFunction = false;
+}
+
+static void compileCallFunction(Context* c, Token t) {
+    codePushInstruction(c, JUMP_SUB);
+    size_t pos = codeGetWritePosition(c);
+    codePushI32(c, addCallFunction(c, t.stringValue, (i32)pos));
+    consumeToken(c, OPEN_ROUND_BRACKET);
+    consumeToken(c, CLOSE_ROUND_BRACKET);
+    consumeNewline(c);
+}
+
 static void compileLine(Context* c, Token token) {
     if(token.type == NEWLINE) {
         c->line++;
@@ -177,6 +276,9 @@ static void compileLine(Context* c, Token token) {
     } else if(token.type == IF) {
         compileIf(c);
         return;
+    } else if(token.type == FUNCTION) {
+        compileFunction(c);
+        return;
     } else if(token.type != LITERAL) {
         unexpectedToken(c, token);
     }
@@ -190,6 +292,8 @@ static void compileLine(Context* c, Token token) {
         codePushInstruction(c, PRINT_NEWLINE);
     } else if(tokenizerPeek(c->tokenizer).type == EQUAL) {
         compileSetVariable(c, s);
+    } else if(tokenizerPeek(c->tokenizer).type == OPEN_ROUND_BRACKET) {
+        compileCallFunction(c, token);
     } else {
         THROW_ERROR("Unexpected literal(%s)", s);
     }
@@ -203,7 +307,7 @@ static void parseTokens(Context* c) {
 
     while(true) {
         Token token = tokenizerNext(c->tokenizer);
-        if(token.type == END) {
+        if(token.type == INVALID) {
             break;
         }
         compileLine(c, token);
@@ -212,6 +316,7 @@ static void parseTokens(Context* c) {
     if(c->variables != nullptr) {
         codeRewriteI32(c, stackVarsLoc, c->variables->index + 1);
     }
+    checkForInvalidFunctions(c);
 }
 
 Error compileFile(Tokenizer* t, Code* code) {

+ 18 - 12
src/Tokenizer.c

@@ -54,20 +54,14 @@ static bool isAlphaNumeric(char c) {
     return isLetter(c) || isNumber(c);
 }
 
-static bool isTokenEnd(char c) {
-    return c == ' ' || c == '\0' || c == '\n';
-}
-
 static const char* tokenizerAddLiteral(TState* t, const char* s) {
     size_t index = 0;
     char buffer[MAX_LITERAL_LENGTH] = {};
     buffer[index++] = *s;
     while(true) {
         char c = *(++s);
-        if(isTokenEnd(c)) {
+        if(!isAlphaNumeric(c)) {
             break;
-        } else if(!isAlphaNumeric(c)) {
-            tInvalidToken(t, c);
         } else if(index >= sizeof(buffer) - 1) {
             THROW_ERROR("Too long literal");
         }
@@ -79,6 +73,10 @@ static const char* tokenizerAddLiteral(TState* t, const char* s) {
         tAddToken(t, THEN);
     } else if(strcmp(buffer, "endif") == 0) {
         tAddToken(t, ENDIF);
+    } else if(strcmp(buffer, "function") == 0) {
+        tAddToken(t, FUNCTION);
+    } else if(strcmp(buffer, "end") == 0) {
+        tAddToken(t, END);
     } else {
         tAddToken(t, LITERAL);
         if(bufferWriteString(&t->tokenizer->buffer, buffer)) {
@@ -94,10 +92,8 @@ static const char* tokenizerAddNumber(TState* t, const char* s) {
     number[index++] = *s;
     while(true) {
         char c = *(++s);
-        if(isTokenEnd(c)) {
+        if(!isNumber(c)) {
             break;
-        } else if(!isNumber(c)) {
-            tInvalidToken(t, c);
         } else if(index >= sizeof(number) - 1) {
             tInvalidNumber(t);
         }
@@ -158,6 +154,12 @@ static void tParseLineString(TState* t, const char* s) {
         } else if(c == '=') {
             tAddToken(t, EQUAL);
             s++;
+        } else if(c == '(') {
+            tAddToken(t, OPEN_ROUND_BRACKET);
+            s++;
+        } else if(c == ')') {
+            tAddToken(t, CLOSE_ROUND_BRACKET);
+            s++;
         } else if(c == '\0') {
             break;
         } else {
@@ -196,7 +198,7 @@ static i32 tReadInt32(Tokenizer* t) {
 }
 
 Token tokenizerNext(Tokenizer* t) {
-    Token token = {.type = END};
+    Token token = {.type = INVALID};
     if(bufferReadU8(&t->buffer, &token.type)) {
         return token;
     }
@@ -254,8 +256,12 @@ void tokenizerPrintToken(const Token* token, char* buffer, size_t n) {
         TOKEN_PRINTER(IF, "If");
         TOKEN_PRINTER(THEN, "Then");
         TOKEN_PRINTER(ENDIF, "EndIf");
-        TOKEN_PRINTER(NEWLINE, "Newline");
+        TOKEN_PRINTER(FUNCTION, "Function");
         TOKEN_PRINTER(END, "End");
+        TOKEN_PRINTER(OPEN_ROUND_BRACKET, "(");
+        TOKEN_PRINTER(CLOSE_ROUND_BRACKET, ")");
+        TOKEN_PRINTER(NEWLINE, "Newline");
+        TOKEN_PRINTER(INVALID, "Invalid");
     }
     snprintf(buffer, n, "Unknown");
 }

+ 5 - 1
src/Tokenizer.h

@@ -13,8 +13,12 @@ typedef enum : u8 {
     IF,
     THEN,
     ENDIF,
+    FUNCTION,
+    END,
+    OPEN_ROUND_BRACKET,
+    CLOSE_ROUND_BRACKET,
     NEWLINE,
-    END
+    INVALID
 } TokenType;
 
 typedef struct {

+ 16 - 0
test/Function.basic

@@ -0,0 +1,16 @@
+function wusi()
+    print "hi there"
+    print "great"
+end
+
+wusi()
+gusi()
+gusi()
+wusi()
+
+function gusi()
+    print "baum"
+    print "wow"
+end
+
+gusi()

+ 10 - 0
test/Function.basic_result

@@ -0,0 +1,10 @@
+hi there
+great
+baum
+wow
+baum
+wow
+hi there
+great
+baum
+wow