Kaynağa Gözat

Functions with arguments, local variables

Kajetan Johannes Hammerle 1 hafta önce
ebeveyn
işleme
9aadce5128
8 değiştirilmiş dosya ile 208 ekleme ve 25 silme
  1. 90 11
      src/Code.c
  2. 4 1
      src/Code.h
  3. 80 11
      src/Compiler.c
  4. 2 0
      src/Main.c
  5. 10 2
      src/Tokenizer.c
  6. 2 0
      src/Tokenizer.h
  7. 17 0
      test/Function2.basic
  8. 3 0
      test/Function2.basic_result

+ 90 - 11
src/Code.c

@@ -31,10 +31,10 @@ void codeReset(Code* c) {
 #define CSTRING_VALUE(value)                               \
     ((Value){.type = VT_CONSTANT_STRING, .data = (value)})
 
-#define SET_ERROR(format, ...)                \
-    snprintf(                                 \
-        c->error.text, sizeof(c->error.text), \
-        format __VA_OPT__(, ) __VA_ARGS__)
+#define SET_ERROR(format, ...)                                 \
+    snprintf(                                                  \
+        c->error.text, sizeof(c->error.text), "%zu | " format, \
+        c->code.readIndex __VA_OPT__(, ) __VA_ARGS__)
 
 [[nodiscard]] static bool codeReadI32(Code* c, i32* i) {
     return bufferReadI32(&c->code, i);
@@ -142,29 +142,57 @@ static bool iJumpIf(Code* c) {
 
 static bool iJumpSub(Code* c) {
     i32 jumpPos = 0;
+    i32 offset = 0;
     if(codeReadI32(c, &jumpPos)) {
         SET_ERROR("JumpSub without position");
         return true;
-    } else if(iPushValue(c, INT_VALUE(codeGetPosition(c)))) {
+    } else if(codeReadI32(c, &offset)) {
+        SET_ERROR("JumpSub without offset");
         return true;
     }
+    i32 returnIndex = (i32)c->stackIndex - (offset + 1);
+    if(returnIndex < 1 && returnIndex >= (i32)c->maxStackSize) {
+        SET_ERROR("JumpSub with invalid return index");
+        return true;
+    }
+    c->stack[returnIndex].data = codeGetPosition(c);
+    c->stack[returnIndex - 1].data = c->localVariableIndex;
+    c->localVariableIndex = returnIndex;
     codeSetPosition(c, (size_t)jumpPos);
     return false;
 }
 
 static bool iReturn(Code* c) {
-    POP_VALUE(a);
-    codeSetPosition(c, (size_t)a.data);
+    i32 popAmount = 0;
+    if(codeReadI32(c, &popAmount)) {
+        SET_ERROR("Return without pop amount");
+        return true;
+    }
+    c->stackIndex -= (size_t)popAmount;
+    POP_VALUE(returnAddress);
+    POP_VALUE(variableIndex);
+    codeSetPosition(c, (size_t)returnAddress.data);
+    c->localVariableIndex = variableIndex.data;
     return false;
 }
 
+static i32 iConvertAddress(Code* c, i32 address) {
+    if(address >= 0) {
+        return address;
+    }
+    return c->localVariableIndex - address;
+}
+
 static bool iReadVariable(Code* c) {
     i32 address = 0;
     if(codeReadI32(c, &address)) {
         SET_ERROR("ReadVariable without address");
         return true;
-    } else if((size_t)address >= c->stackIndex) {
-        SET_ERROR("ReadVariable with invalid address");
+    }
+    address = iConvertAddress(c, address);
+    if((size_t)address >= c->stackIndex) {
+        SET_ERROR(
+            "ReadVariable with invalid address %d %zu", address, c->stackIndex);
         return true;
     }
     return iPushValue(c, c->stack[address]);
@@ -175,7 +203,9 @@ static bool iSetVariable(Code* c) {
     if(codeReadI32(c, &address)) {
         SET_ERROR("SetVariable without address");
         return true;
-    } else if((size_t)address >= c->stackIndex) {
+    }
+    address = iConvertAddress(c, address);
+    if((size_t)address >= c->stackIndex) {
         SET_ERROR("SetVariable with invalid address");
         return true;
     }
@@ -204,7 +234,7 @@ static bool execute(Code* c, Instruction command) {
     switch(command) {
         case ADD: return iAdd(c);
         case PUSH_CONSTANT_STRING: return iPushConstantString(c);
-        case PUSH_INT64: return iPushInt(c);
+        case PUSH_INT32: return iPushInt(c);
         case PRINT: return iPrint(c);
         case PRINT_NEWLINE: return iPrintNewline();
         case JUMP: return iJump(c);
@@ -235,3 +265,52 @@ bool codeHasRunError(const Code* code) {
 const char* codeGetRunError(const Code* code) {
     return code->error.text;
 }
+
+#define DUMP(name)                                \
+    case name: fprintf(stderr, #name "\n"); break
+#define DUMP_INT(name)                     \
+    case name: {                           \
+        i32 v = 0;                         \
+        (void)codeReadI32(&c, &v);         \
+        fprintf(stderr, #name " %d\n", v); \
+        break;                             \
+    } break
+#define DUMP_INT2(name)                                \
+    case name: {                                       \
+        i32 v[2] = {};                                 \
+        (void)codeReadI32(&c, v);                      \
+        (void)codeReadI32(&c, v + 1);                  \
+        fprintf(stderr, #name " %d %d\n", v[0], v[1]); \
+        break;                                         \
+    } break
+
+void codeDump(const Code* code) {
+    Code c = *code;
+    while(true) {
+        size_t start = c.code.readIndex;
+        Instruction i = STOP;
+        if(bufferReadU8(&c.code, &i)) {
+            return;
+        }
+        fprintf(stderr, "%4zu | ", start);
+        switch(i) {
+            DUMP(ADD);
+            DUMP_INT(PUSH_INT32);
+            DUMP(PRINT);
+            DUMP(PRINT_NEWLINE);
+            DUMP_INT(JUMP);
+            DUMP_INT(JUMP_ON_0);
+            DUMP_INT2(JUMP_SUB);
+            DUMP_INT(RETURN);
+            DUMP_INT(READ_VARIABLE);
+            DUMP_INT(SET_VARIABLE);
+            DUMP_INT(PUSH_STACK_VARIABLES);
+            DUMP(STOP);
+            case PUSH_CONSTANT_STRING:
+                fprintf(
+                    stderr, "PUSH_CONSTANT_STRING %s\n",
+                    codeReadConstantString(&c));
+                break;
+        }
+    }
+}

+ 4 - 1
src/Code.h

@@ -20,7 +20,7 @@ static_assert(sizeof(Value) == 8);
 typedef enum : u8 {
     ADD,
     PUSH_CONSTANT_STRING,
-    PUSH_INT64,
+    PUSH_INT32,
     PRINT,
     PRINT_NEWLINE,
     JUMP,
@@ -39,6 +39,7 @@ typedef struct {
     Value* stack;
     size_t maxStackSize;
     size_t stackIndex;
+    i32 localVariableIndex;
 } Code;
 
 void codeInit(Code* code);
@@ -49,4 +50,6 @@ void codeRun(Code* code);
 bool codeHasRunError(const Code* code);
 const char* codeGetRunError(const Code* code);
 
+void codeDump(const Code* code);
+
 #endif

+ 80 - 11
src/Compiler.c

@@ -75,8 +75,15 @@ static void codeRewriteI32(Context* c, size_t pos, i32 i) {
     c->code->code.writeIndex = oldPos;
 }
 
-static i32 addVariable(Context* c, const char* name) {
+static bool hasLocalVar(const Context* c) {
+    return c->variables != nullptr && c->variables->index < 0;
+}
+
+static i32 addVariable(Context* c, const char* name, bool forceGlobal) {
     for(Variable* v = c->variables; v != nullptr; v = v->next) {
+        if(!forceGlobal && c->inFunction && v->index >= 0) {
+            break;
+        }
         if(strcmp(v->name, name) == 0) {
             return v->index;
         }
@@ -87,7 +94,11 @@ static i32 addVariable(Context* c, const char* name) {
         THROW_ERROR("Too less memory for variables");
     }
     v->next = c->variables;
-    v->index = c->variables != nullptr ? c->variables->index + 1 : 0;
+    if(c->inFunction) {
+        v->index = hasLocalVar(c) ? c->variables->index - 1 : -1;
+    } else {
+        v->index = c->variables != nullptr ? c->variables->index + 1 : 0;
+    }
     memcpy(v->name, name, l);
     c->variables = v;
     return v->index;
@@ -176,18 +187,25 @@ static Token consumeNewline(Context* c) {
     return t;
 }
 
+static void compileReadVariable(Context* c, Token token, bool forceGlobal) {
+    i32 index = addVariable(c, token.stringValue, forceGlobal);
+    codePushInstruction(c, READ_VARIABLE);
+    codePushI32(c, index);
+}
+
 static void compileConstant(Context* c) {
     Token token = tokenizerNext(c->tokenizer);
     if(token.type == STRING) {
         codePushInstruction(c, PUSH_CONSTANT_STRING);
         codePushConstantString(c, token.stringValue);
     } else if(token.type == INT32) {
-        codePushInstruction(c, PUSH_INT64);
+        codePushInstruction(c, PUSH_INT32);
         codePushI32(c, token.intValue);
     } else if(token.type == LITERAL) {
-        i32 index = addVariable(c, token.stringValue);
-        codePushInstruction(c, READ_VARIABLE);
-        codePushI32(c, index);
+        compileReadVariable(c, token, false);
+    } else if(token.type == DOLLAR) {
+        token = consumeToken(c, LITERAL);
+        compileReadVariable(c, token, true);
     } else {
         unexpectedToken(c, token);
     }
@@ -226,10 +244,10 @@ static void compileIf(Context* c) {
     codeRewriteI32(c, posIndex, (i32)codeGetWritePosition(c));
 }
 
-static void compileSetVariable(Context* c, const char* name) {
+static void compileSetVariable(Context* c, const char* name, bool forceGlobal) {
     consumeToken(c, EQUAL);
     compileExpression(c);
-    i32 index = addVariable(c, name);
+    i32 index = addVariable(c, name, forceGlobal);
     codePushInstruction(c, SET_VARIABLE);
     codePushI32(c, index);
 }
@@ -239,13 +257,32 @@ static void compileFunction(Context* c) {
         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));
+
+    codePushInstruction(c, PUSH_STACK_VARIABLES);
+    size_t stackVarsLoc = codeGetWritePosition(c);
+    codePushI32(c, 0);
+
     consumeToken(c, OPEN_ROUND_BRACKET);
+    i32 vars = 0;
+    Variable* resetVar = c->variables;
+    while(true) {
+        if(tokenizerPeek(c->tokenizer).type != LITERAL) {
+            break;
+        }
+        Token varToken = consumeToken(c, LITERAL);
+        addVariable(c, varToken.stringValue, false);
+        vars++;
+        if(tokenizerPeek(c->tokenizer).type == COMMA) {
+            consumeToken(c, COMMA);
+        }
+    }
     consumeToken(c, CLOSE_ROUND_BRACKET);
     while(true) {
         if(tokenizerPeek(c->tokenizer).type == END) {
@@ -254,18 +291,46 @@ static void compileFunction(Context* c) {
         compileLine(c, tokenizerNext(c->tokenizer));
     }
     consumeToken(c, END);
+    i32 popVars = hasLocalVar(c) ? -c->variables->index : 0;
     codePushInstruction(c, RETURN);
+    codePushI32(c, popVars);
+    codeRewriteI32(c, stackVarsLoc, popVars - vars);
+
+    while(c->variables != resetVar) {
+        Variable* next = c->variables->next;
+        memoryFree(c->variables);
+        c->variables = next;
+    }
 
     codeRewriteI32(c, pos, (i32)codeGetWritePosition(c));
     c->inFunction = false;
 }
 
 static void compileCallFunction(Context* c, Token t) {
+    // used to store return address and variable index
+    codePushInstruction(c, PUSH_INT32);
+    codePushI32(c, 0);
+    codePushInstruction(c, PUSH_INT32);
+    codePushI32(c, 0);
+
+    consumeToken(c, OPEN_ROUND_BRACKET);
+    i32 offset = 0;
+    while(true) {
+        if(tokenizerPeek(c->tokenizer).type == CLOSE_ROUND_BRACKET) {
+            break;
+        }
+        offset++;
+        compileExpression(c);
+        if(tokenizerPeek(c->tokenizer).type == COMMA) {
+            consumeToken(c, COMMA);
+        }
+    }
+    consumeToken(c, CLOSE_ROUND_BRACKET);
+
     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);
+    codePushI32(c, offset);
     consumeNewline(c);
 }
 
@@ -279,6 +344,10 @@ static void compileLine(Context* c, Token token) {
     } else if(token.type == FUNCTION) {
         compileFunction(c);
         return;
+    } else if(token.type == DOLLAR) {
+        token = consumeToken(c, LITERAL);
+        compileSetVariable(c, token.stringValue, true);
+        return;
     } else if(token.type != LITERAL) {
         unexpectedToken(c, token);
     }
@@ -291,7 +360,7 @@ static void compileLine(Context* c, Token token) {
         consumeNewline(c);
         codePushInstruction(c, PRINT_NEWLINE);
     } else if(tokenizerPeek(c->tokenizer).type == EQUAL) {
-        compileSetVariable(c, s);
+        compileSetVariable(c, s, false);
     } else if(tokenizerPeek(c->tokenizer).type == OPEN_ROUND_BRACKET) {
         compileCallFunction(c, token);
     } else {

+ 2 - 0
src/Main.c

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

+ 10 - 2
src/Tokenizer.c

@@ -148,6 +148,12 @@ static void tParseLineString(TState* t, const char* s) {
             break;
         } else if(c == ' ') {
             s++;
+        } else if(c == ',') {
+            tAddToken(t, COMMA);
+            s++;
+        } else if(c == '$') {
+            tAddToken(t, DOLLAR);
+            s++;
         } else if(c == '+') {
             tAddToken(t, PLUS);
             s++;
@@ -251,8 +257,10 @@ void tokenizerPrintToken(const Token* token, char* buffer, size_t n) {
         TOKEN_PRINTER(LITERAL, "Literal(%s)", token->stringValue);
         TOKEN_PRINTER(INT32, "Int32(%d)", token->intValue);
         TOKEN_PRINTER(STRING, "String(%s)", token->stringValue);
-        TOKEN_PRINTER(PLUS, "Plus");
-        TOKEN_PRINTER(EQUAL, "Equal");
+        TOKEN_PRINTER(DOLLAR, "$");
+        TOKEN_PRINTER(COMMA, ",");
+        TOKEN_PRINTER(PLUS, "+");
+        TOKEN_PRINTER(EQUAL, "=");
         TOKEN_PRINTER(IF, "If");
         TOKEN_PRINTER(THEN, "Then");
         TOKEN_PRINTER(ENDIF, "EndIf");

+ 2 - 0
src/Tokenizer.h

@@ -8,6 +8,8 @@ typedef enum : u8 {
     LITERAL,
     INT32,
     STRING,
+    COMMA,
+    DOLLAR,
     PLUS,
     EQUAL,
     IF,

+ 17 - 0
test/Function2.basic

@@ -0,0 +1,17 @@
+c = 3
+d = 5
+
+function wusi(a, b)
+    d = 10
+    print a + b + $c + d
+end
+
+wusi(5, 6)
+print d
+
+function mod()
+    $d = 12
+end
+
+mod()
+print d

+ 3 - 0
test/Function2.basic_result

@@ -0,0 +1,3 @@
+24
+5
+12