4 Комити b2444aaa97 ... 1a24286cde

Аутор SHA1 Порука Датум
  Kajetan Johannes Hammerle 1a24286cde Refactoring пре 2 недеља
  Kajetan Johannes Hammerle 22a7f342ef Add comparison and logical features пре 2 недеља
  Kajetan Johannes Hammerle e548287bdb Functions with return value, improved checking пре 2 недеља
  Kajetan Johannes Hammerle 9aadce5128 Functions with arguments, local variables пре 2 недеља

+ 195 - 13
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);
@@ -85,7 +85,78 @@ static bool iPopValue(Code* c, Value* v) {
 static bool iAdd(Code* c) {
     POP_VALUE(a);
     POP_VALUE(b);
-    return iPushValue(c, INT_VALUE(a.data + b.data));
+    return iPushValue(c, INT_VALUE(b.data + a.data));
+}
+
+static bool iSub(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data - a.data));
+}
+
+static bool iMul(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data * a.data));
+}
+
+static bool iDiv(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data / a.data));
+}
+
+static bool iAnd(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data && a.data));
+}
+
+static bool iOr(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data || a.data));
+}
+
+static bool iNot(Code* c) {
+    POP_VALUE(a);
+    return iPushValue(c, INT_VALUE(!a.data));
+}
+
+static bool iEqual(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data == a.data));
+}
+
+static bool iNotEqual(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data != a.data));
+}
+
+static bool iGreater(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data > a.data));
+}
+
+static bool iSmaller(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data < a.data));
+}
+
+static bool iGreaterOrEqual(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data >= a.data));
+}
+
+static bool iSmallerOrEqual(Code* c) {
+    POP_VALUE(a);
+    POP_VALUE(b);
+    return iPushValue(c, INT_VALUE(b.data <= a.data));
 }
 
 static bool iPushConstantString(Code* c) {
@@ -142,29 +213,65 @@ 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 bool iReturnValue(Code* c) {
+    POP_VALUE(value);
+    if(iReturn(c)) {
+        return true;
+    }
+    return iPushValue(c, value);
+}
+
+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 +282,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;
     }
@@ -203,17 +312,29 @@ static bool iPushStackVariables(Code* c) {
 static bool execute(Code* c, Instruction command) {
     switch(command) {
         case ADD: return iAdd(c);
+        case SUB: return iSub(c);
+        case MUL: return iMul(c);
+        case DIV: return iDiv(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);
         case JUMP_ON_0: return iJumpIf(c);
         case JUMP_SUB: return iJumpSub(c);
-        case RETURN: return iReturn(c);
+        case RETURN_VALUE: return iReturnValue(c);
         case READ_VARIABLE: return iReadVariable(c);
         case SET_VARIABLE: return iSetVariable(c);
         case PUSH_STACK_VARIABLES: return iPushStackVariables(c);
+        case AND: return iAnd(c);
+        case OR: return iOr(c);
+        case NOT: return iNot(c);
+        case EQUAL: return iEqual(c);
+        case NOT_EQUAL: return iNotEqual(c);
+        case GREATER: return iGreater(c);
+        case SMALLER: return iSmaller(c);
+        case GREATER_OR_EQUAL: return iGreaterOrEqual(c);
+        case SMALLER_OR_EQUAL: return iSmallerOrEqual(c);
         case STOP: return true;
     }
     return false;
@@ -235,3 +356,64 @@ 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(SUB);
+            DUMP(MUL);
+            DUMP(DIV);
+            DUMP_INT(PUSH_INT32);
+            DUMP(PRINT);
+            DUMP(PRINT_NEWLINE);
+            DUMP_INT(JUMP);
+            DUMP_INT(JUMP_ON_0);
+            DUMP_INT2(JUMP_SUB);
+            DUMP_INT(RETURN_VALUE);
+            DUMP_INT(READ_VARIABLE);
+            DUMP_INT(SET_VARIABLE);
+            DUMP_INT(PUSH_STACK_VARIABLES);
+            DUMP(AND);
+            DUMP(OR);
+            DUMP(NOT);
+            DUMP(EQUAL);
+            DUMP(NOT_EQUAL);
+            DUMP(GREATER);
+            DUMP(SMALLER);
+            DUMP(GREATER_OR_EQUAL);
+            DUMP(SMALLER_OR_EQUAL);
+            DUMP(STOP);
+            case PUSH_CONSTANT_STRING:
+                fprintf(
+                    stderr, "PUSH_CONSTANT_STRING %s\n",
+                    codeReadConstantString(&c));
+                break;
+        }
+    }
+}

+ 17 - 2
src/Code.h

@@ -19,17 +19,29 @@ static_assert(sizeof(Value) == 8);
 
 typedef enum : u8 {
     ADD,
+    SUB,
+    MUL,
+    DIV,
     PUSH_CONSTANT_STRING,
-    PUSH_INT64,
+    PUSH_INT32,
     PRINT,
     PRINT_NEWLINE,
     JUMP,
     JUMP_ON_0,
     JUMP_SUB,
-    RETURN,
+    RETURN_VALUE,
     READ_VARIABLE,
     SET_VARIABLE,
     PUSH_STACK_VARIABLES,
+    AND,
+    OR,
+    NOT,
+    EQUAL,
+    NOT_EQUAL,
+    GREATER,
+    SMALLER,
+    GREATER_OR_EQUAL,
+    SMALLER_OR_EQUAL,
     STOP
 } Instruction;
 
@@ -39,6 +51,7 @@ typedef struct {
     Value* stack;
     size_t maxStackSize;
     size_t stackIndex;
+    i32 localVariableIndex;
 } Code;
 
 void codeInit(Code* code);
@@ -49,4 +62,6 @@ void codeRun(Code* code);
 bool codeHasRunError(const Code* code);
 const char* codeGetRunError(const Code* code);
 
+void codeDump(const Code* code);
+
 #endif

+ 296 - 95
src/Compiler.c

@@ -22,9 +22,18 @@ struct Function {
     Function* next;
     i32 address;
     i32 valueAddress;
+    i32 arguments;
     char name[];
 };
 
+typedef struct Return Return;
+
+struct Return {
+    Return* next;
+    i32 address;
+    bool hasArgument;
+};
+
 typedef struct {
     Tokenizer* tokenizer;
     Code* code;
@@ -33,6 +42,7 @@ typedef struct {
     jmp_buf jump;
     Variable* variables;
     Function* functions;
+    Return* returns;
     bool inFunction;
 } Context;
 
@@ -68,6 +78,13 @@ static size_t codeGetWritePosition(const Context* c) {
     return c->code->code.writeIndex;
 }
 
+static size_t codePushInstructionI32(Context* c, Instruction i, i32 v) {
+    codePushInstruction(c, i);
+    size_t pos = codeGetWritePosition(c);
+    codePushI32(c, v);
+    return pos;
+}
+
 static void codeRewriteI32(Context* c, size_t pos, i32 i) {
     size_t oldPos = codeGetWritePosition(c);
     c->code->code.writeIndex = pos;
@@ -75,8 +92,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,14 +111,19 @@ 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;
 }
 
 static void addRawFunction(
-    Context* c, const char* name, i32 address, i32 valueAddress) {
+    Context* c, const char* name, i32 address, i32 valueAddress,
+    i32 arguments) {
     size_t l = strlen(name) + 1;
     Function* f = memoryAllocate(sizeof(Function) + l);
     if(f == nullptr) {
@@ -103,30 +132,38 @@ static void addRawFunction(
     f->next = c->functions;
     f->address = address;
     f->valueAddress = valueAddress;
+    f->arguments = arguments;
     memcpy(f->name, name, l);
     c->functions = f;
 }
 
-static void addFunction(Context* c, const char* name, i32 address) {
+static void addFunction(
+    Context* c, const char* name, i32 address, i32 arguments) {
     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);
+        } else if(f->arguments != arguments) {
+            THROW_ERROR("Function '%s' with inconsistent arguments", name);
         }
         f->address = address;
         codeRewriteI32(c, (size_t)f->valueAddress, address);
     }
-    addRawFunction(c, name, address, -1);
+    addRawFunction(c, name, address, -1, arguments);
 }
 
-static i32 addCallFunction(Context* c, const char* name, i32 address) {
+static i32 addCallFunction(
+    Context* c, const char* name, i32 address, i32 arguments) {
     for(Function* f = c->functions; f != nullptr; f = f->next) {
         if(strcmp(f->name, name) == 0 && f->address >= 0) {
+            if(f->arguments != arguments) {
+                THROW_ERROR("Function '%s' with inconsistent arguments", name);
+            }
             return f->address;
         }
     }
-    addRawFunction(c, name, -1, address);
+    addRawFunction(c, name, -1, address, arguments);
     return -1;
 }
 
@@ -138,100 +175,228 @@ static void checkForInvalidFunctions(Context* c) {
     }
 }
 
-static void cleanContext(Context* c) {
-    Variable* v = c->variables;
-    while(v != nullptr) {
-        Variable* next = v->next;
-        memoryFree(v);
-        v = next;
+static void addReturn(Context* c, i32 address, bool hasArgument) {
+    if(c->returns != nullptr && c->returns->hasArgument != hasArgument) {
+        THROW_ERROR("Inconsistent returns in function");
     }
-    c->variables = nullptr;
-
-    Function* f = c->functions;
-    while(f != nullptr) {
-        Function* next = f->next;
-        memoryFree(f);
-        f = next;
+    Return* r = memoryAllocate(sizeof(Return));
+    if(r == nullptr) {
+        THROW_ERROR("Too less memory for returns");
     }
-    c->functions = nullptr;
+    r->next = c->returns;
+    r->hasArgument = hasArgument;
+    r->address = address;
+    c->returns = r;
+}
+
+#define CLEAN_LIST(Type, field)   \
+    do {                          \
+        Type* v = c->field;       \
+        while(v != nullptr) {     \
+            Type* next = v->next; \
+            memoryFree(v);        \
+            v = next;             \
+        }                         \
+        c->field = nullptr;       \
+    } while(false)
+
+static void cleanReturns(Context* c) {
+    CLEAN_LIST(Return, returns);
+}
+
+static void cleanContext(Context* c) {
+    CLEAN_LIST(Variable, variables);
+    CLEAN_LIST(Function, functions);
+    cleanReturns(c);
 }
 
-[[noreturn]] static void unexpectedToken(Context* c, Token token) {
+[[noreturn]] static void unexpectedToken(Context* c, Token token, int line) {
     char buffer[128];
     tokenizerPrintToken(&token, buffer, sizeof(buffer));
-    THROW_ERROR("Unexpected %s token", buffer);
+    THROW_ERROR("Unexpected %s token %d", buffer, line);
 }
 
 static Token consumeToken(Context* c, TokenType type) {
     Token actual = tokenizerNext(c->tokenizer);
     if(actual.type != type) {
-        unexpectedToken(c, actual);
+        unexpectedToken(c, actual, __LINE__);
     }
     return actual;
 }
 
-static Token consumeNewline(Context* c) {
-    Token t = consumeToken(c, NEWLINE);
+static bool peekToken(Context* c, TokenType type) {
+    return tokenizerPeek(c->tokenizer).type == type;
+}
+
+static bool consumeTokenIf(Context* c, TokenType type) {
+    if(peekToken(c, type)) {
+        consumeToken(c, type);
+        return true;
+    }
+    return false;
+}
+
+static void consumeNewline(Context* c) {
+    if(peekToken(c, TT_INVALID)) {
+        return;
+    }
+    consumeToken(c, TT_NEWLINE);
     c->line++;
-    return t;
 }
 
+static void compileReadVariable(Context* c, Token token, bool forceGlobal) {
+    i32 index = addVariable(c, token.stringValue, forceGlobal);
+    codePushInstructionI32(c, READ_VARIABLE, index);
+}
+
+static void compileCallFunction(Context* c, Token t);
+
 static void compileConstant(Context* c) {
     Token token = tokenizerNext(c->tokenizer);
-    if(token.type == STRING) {
+    if(token.type == TT_STRING) {
         codePushInstruction(c, PUSH_CONSTANT_STRING);
         codePushConstantString(c, token.stringValue);
-    } else if(token.type == INT32) {
-        codePushInstruction(c, PUSH_INT64);
-        codePushI32(c, token.intValue);
-    } else if(token.type == LITERAL) {
-        i32 index = addVariable(c, token.stringValue);
-        codePushInstruction(c, READ_VARIABLE);
-        codePushI32(c, index);
+    } else if(token.type == TT_INT32) {
+        codePushInstructionI32(c, PUSH_INT32, token.intValue);
+    } else if(token.type == TT_LITERAL) {
+        if(peekToken(c, TT_OPEN_ROUND_BRACKET)) {
+            compileCallFunction(c, token);
+        } else {
+            compileReadVariable(c, token, false);
+        }
+    } else if(token.type == TT_DOLLAR) {
+        token = consumeToken(c, TT_LITERAL);
+        compileReadVariable(c, token, true);
     } else {
-        unexpectedToken(c, token);
+        unexpectedToken(c, token, __LINE__);
     }
 }
 
-static void compileAdd(Context* c) {
-    compileConstant(c);
-    while(tokenizerPeek(c->tokenizer).type == PLUS) {
-        tokenizerNext(c->tokenizer);
+static void compileUnary(Context* c) {
+    if(consumeTokenIf(c, TT_NOT)) {
+        compileUnary(c);
+        codePushInstruction(c, NOT);
+    } else {
         compileConstant(c);
-        codePushInstruction(c, ADD);
     }
 }
 
-static void compileExpression(Context* c) {
+static void compileMul(Context* c) {
+    compileUnary(c);
+    while(true) {
+        if(consumeTokenIf(c, TT_MULTIPLY)) {
+            compileUnary(c);
+            codePushInstruction(c, MUL);
+        } else if(consumeTokenIf(c, TT_DIVIDE)) {
+            compileUnary(c);
+            codePushInstruction(c, DIV);
+        } else {
+            break;
+        }
+    }
+}
+
+static void compileAdd(Context* c) {
+    compileMul(c);
+    while(true) {
+        if(consumeTokenIf(c, TT_ADD)) {
+            compileMul(c);
+            codePushInstruction(c, ADD);
+        } else if(consumeTokenIf(c, TT_SUBTRACT)) {
+            compileMul(c);
+            codePushInstruction(c, SUB);
+        } else {
+            break;
+        }
+    }
+}
+
+static void compileCompare(Context* c) {
     compileAdd(c);
+    while(true) {
+        if(consumeTokenIf(c, TT_EQUAL)) {
+            compileAdd(c);
+            codePushInstruction(c, EQUAL);
+        } else if(consumeTokenIf(c, TT_NOT_EQUAL)) {
+            compileAdd(c);
+            codePushInstruction(c, NOT_EQUAL);
+        } else if(consumeTokenIf(c, TT_GREATER)) {
+            compileAdd(c);
+            codePushInstruction(c, GREATER);
+        } else if(consumeTokenIf(c, TT_SMALLER)) {
+            compileAdd(c);
+            codePushInstruction(c, SMALLER);
+        } else if(consumeTokenIf(c, TT_GREATER_OR_EQUAL)) {
+            compileAdd(c);
+            codePushInstruction(c, GREATER_OR_EQUAL);
+        } else if(consumeTokenIf(c, TT_SMALLER_OR_EQUAL)) {
+            compileAdd(c);
+            codePushInstruction(c, SMALLER_OR_EQUAL);
+        } else {
+            break;
+        }
+    }
+}
+
+static void compileLogical(Context* c) {
+    compileCompare(c);
+    while(true) {
+        if(consumeTokenIf(c, TT_AND)) {
+            compileCompare(c);
+            codePushInstruction(c, AND);
+        } else if(consumeTokenIf(c, TT_OR)) {
+            compileCompare(c);
+            codePushInstruction(c, OR);
+        } else {
+            break;
+        }
+    }
+}
+
+static void compileExpression(Context* c) {
+    compileLogical(c);
 }
 
 static void compileLine(Context* c, Token token);
 
 static void compileIf(Context* c) {
     compileExpression(c);
-    codePushInstruction(c, JUMP_ON_0);
-    size_t posIndex = codeGetWritePosition(c);
-    codePushI32(c, 0);
-    consumeToken(c, THEN);
+    size_t posIndex = codePushInstructionI32(c, JUMP_ON_0, 0);
     consumeNewline(c);
-    while(true) {
-        if(tokenizerPeek(c->tokenizer).type == ENDIF) {
-            break;
-        }
+    while(!peekToken(c, TT_END)) {
         compileLine(c, tokenizerNext(c->tokenizer));
     }
-    consumeToken(c, ENDIF);
+    consumeToken(c, TT_END);
     consumeNewline(c);
     codeRewriteI32(c, posIndex, (i32)codeGetWritePosition(c));
 }
 
-static void compileSetVariable(Context* c, const char* name) {
-    consumeToken(c, EQUAL);
+static void compileSetVariable(Context* c, const char* name, bool forceGlobal) {
+    consumeToken(c, TT_ASSIGN);
     compileExpression(c);
-    i32 index = addVariable(c, name);
-    codePushInstruction(c, SET_VARIABLE);
-    codePushI32(c, index);
+    codePushInstructionI32(c, SET_VARIABLE, addVariable(c, name, forceGlobal));
+}
+
+static i32 compileFunctionArguments(Context* c) {
+    consumeToken(c, TT_OPEN_ROUND_BRACKET);
+    i32 vars = 0;
+    while(!peekToken(c, TT_CLOSE_ROUND_BRACKET)) {
+        if(vars > 0) {
+            consumeToken(c, TT_COMMA);
+        }
+        Token varToken = consumeToken(c, TT_LITERAL);
+        addVariable(c, varToken.stringValue, false);
+        vars++;
+    }
+    consumeToken(c, TT_CLOSE_ROUND_BRACKET);
+    return vars;
+}
+
+static void rewriteReturns(Context* c, i32 address) {
+    for(Return* r = c->returns; r != nullptr; r = r->next) {
+        codeRewriteI32(c, (size_t)r->address, address);
+    }
+    cleanReturns(c);
 }
 
 static void compileFunction(Context* c) {
@@ -239,75 +404,111 @@ 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));
-    consumeToken(c, OPEN_ROUND_BRACKET);
-    consumeToken(c, CLOSE_ROUND_BRACKET);
-    while(true) {
-        if(tokenizerPeek(c->tokenizer).type == END) {
-            break;
-        }
+    size_t functionEnd = codePushInstructionI32(c, JUMP, 0);
+    i32 functionStart = (i32)codeGetWritePosition(c);
+    Token t = consumeToken(c, TT_LITERAL);
+    size_t stackVarsLoc = codePushInstructionI32(c, PUSH_STACK_VARIABLES, 0);
+    Variable* resetVar = c->variables;
+
+    i32 vars = compileFunctionArguments(c);
+    addFunction(c, t.stringValue, functionStart, vars);
+    while(!peekToken(c, TT_END)) {
         compileLine(c, tokenizerNext(c->tokenizer));
     }
-    consumeToken(c, END);
-    codePushInstruction(c, RETURN);
+    consumeToken(c, TT_END);
+    i32 popVars = hasLocalVar(c) ? -c->variables->index : 0;
+    codePushInstructionI32(c, PUSH_INT32, 0);
+    codePushInstructionI32(c, RETURN_VALUE, popVars);
+    rewriteReturns(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));
+    codeRewriteI32(c, functionEnd, (i32)codeGetWritePosition(c));
     c->inFunction = false;
 }
 
 static void compileCallFunction(Context* c, Token t) {
+    // used to store return address and variable index
+    codePushInstructionI32(c, PUSH_INT32, 0);
+    codePushInstructionI32(c, PUSH_INT32, 0);
+
+    consumeToken(c, TT_OPEN_ROUND_BRACKET);
+    i32 offset = 0;
+    while(!peekToken(c, TT_CLOSE_ROUND_BRACKET)) {
+        if(offset > 0) {
+            consumeToken(c, TT_COMMA);
+        }
+        offset++;
+        compileExpression(c);
+    }
+    consumeToken(c, TT_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);
-    consumeNewline(c);
+    codePushI32(c, addCallFunction(c, t.stringValue, (i32)pos, offset));
+    codePushI32(c, offset);
 }
 
-static void compileLine(Context* c, Token token) {
-    if(token.type == NEWLINE) {
-        c->line++;
-        return;
-    } else if(token.type == IF) {
-        compileIf(c);
-        return;
-    } else if(token.type == FUNCTION) {
-        compileFunction(c);
-        return;
-    } else if(token.type != LITERAL) {
-        unexpectedToken(c, token);
+static void compileReturn(Context* c) {
+    if(peekToken(c, TT_NEWLINE)) {
+        consumeNewline(c);
+        codePushInstructionI32(c, PUSH_INT32, 0);
+    } else {
+        compileExpression(c);
     }
+    i32 address = (i32)codePushInstructionI32(c, RETURN_VALUE, 0);
+    addReturn(c, address, true);
+}
+
+static void compileLineLiteral(Context* c, Token token) {
     const char* s = token.stringValue;
     if(strcmp(s, "print") == 0) {
-        while(tokenizerPeek(c->tokenizer).type != NEWLINE) {
+        while(!peekToken(c, TT_NEWLINE)) {
             compileExpression(c);
             codePushInstruction(c, PRINT);
         }
         consumeNewline(c);
         codePushInstruction(c, PRINT_NEWLINE);
-    } else if(tokenizerPeek(c->tokenizer).type == EQUAL) {
-        compileSetVariable(c, s);
-    } else if(tokenizerPeek(c->tokenizer).type == OPEN_ROUND_BRACKET) {
+    } else if(peekToken(c, TT_ASSIGN)) {
+        compileSetVariable(c, s, false);
+    } else if(peekToken(c, TT_OPEN_ROUND_BRACKET)) {
         compileCallFunction(c, token);
     } else {
         THROW_ERROR("Unexpected literal(%s)", s);
     }
 }
 
+static void compileLine(Context* c, Token token) {
+    if(token.type == TT_NEWLINE) {
+        c->line++;
+    } else if(token.type == TT_IF) {
+        compileIf(c);
+    } else if(token.type == TT_FUNCTION) {
+        compileFunction(c);
+    } else if(token.type == TT_RETURN) {
+        compileReturn(c);
+    } else if(token.type == TT_DOLLAR) {
+        token = consumeToken(c, TT_LITERAL);
+        compileSetVariable(c, token.stringValue, true);
+    } else if(token.type != TT_LITERAL) {
+        unexpectedToken(c, token, __LINE__);
+    } else {
+        compileLineLiteral(c, token);
+    }
+}
+
 static void parseTokens(Context* c) {
     codeReset(c->code);
-    codePushInstruction(c, PUSH_STACK_VARIABLES);
-    size_t stackVarsLoc = codeGetWritePosition(c);
-    codePushI32(c, 0);
+    size_t stackVarsLoc = codePushInstructionI32(c, PUSH_STACK_VARIABLES, 0);
 
     while(true) {
         Token token = tokenizerNext(c->tokenizer);
-        if(token.type == INVALID) {
+        if(token.type == TT_INVALID) {
             break;
         }
         compileLine(c, token);

+ 7 - 5
src/Main.c

@@ -11,9 +11,9 @@ int main(int argCount, const char** args) {
         return 0;
     }
 
-    static char heap[1000];
+    static char heap[2000];
     memoryInit(heap, sizeof(heap));
-    memoryDump();
+    // memoryDump();
 
     Tokenizer t;
     Error e = tokenizerInit(&t, args[1]);
@@ -30,14 +30,16 @@ int main(int argCount, const char** args) {
     if(hasError(&e)) {
         puts(e.text);
     } else {
-        memoryDump();
+        // codeDump(&code);
+        //   return 0;
+        // memoryDump();
         codeRun(&code);
         if(codeHasRunError(&code)) {
             puts(codeGetRunError(&code));
         }
-        memoryDump();
+        // memoryDump();
     }
     codeDestroy(&code);
-    memoryDump();
+    // memoryDump();
     return 0;
 }

+ 80 - 43
src/Tokenizer.c

@@ -68,17 +68,15 @@ static const char* tokenizerAddLiteral(TState* t, const char* s) {
         buffer[index++] = c;
     }
     if(strcmp(buffer, "if") == 0) {
-        tAddToken(t, IF);
-    } else if(strcmp(buffer, "then") == 0) {
-        tAddToken(t, THEN);
-    } else if(strcmp(buffer, "endif") == 0) {
-        tAddToken(t, ENDIF);
+        tAddToken(t, TT_IF);
     } else if(strcmp(buffer, "function") == 0) {
-        tAddToken(t, FUNCTION);
+        tAddToken(t, TT_FUNCTION);
     } else if(strcmp(buffer, "end") == 0) {
-        tAddToken(t, END);
+        tAddToken(t, TT_END);
+    } else if(strcmp(buffer, "return") == 0) {
+        tAddToken(t, TT_RETURN);
     } else {
-        tAddToken(t, LITERAL);
+        tAddToken(t, TT_LITERAL);
         if(bufferWriteString(&t->tokenizer->buffer, buffer)) {
             tTooMuchTokens(t);
         }
@@ -105,7 +103,7 @@ static const char* tokenizerAddNumber(TState* t, const char* s) {
     if(errno != 0 || *end != '\0' || i > INT32_MAX || i < INT32_MIN) {
         tInvalidNumber(t);
     }
-    tAddToken(t, INT32);
+    tAddToken(t, TT_INT32);
     if(bufferWriteI32(&t->tokenizer->buffer, (i32)i)) {
         tTooMuchTokens(t);
     }
@@ -113,7 +111,7 @@ static const char* tokenizerAddNumber(TState* t, const char* s) {
 }
 
 static const char* tokenizerAddString(TState* t, const char* s) {
-    tAddToken(t, STRING);
+    tAddToken(t, TT_STRING);
     size_t index = 0;
     char buffer[MAX_STRING_LENGTH] = {};
     while(true) {
@@ -134,6 +132,22 @@ static const char* tokenizerAddString(TState* t, const char* s) {
     return s;
 }
 
+#define SIMPLE_TOKEN(ch, token) \
+    else if(c == ch) {          \
+        tAddToken(t, token);    \
+        s++;                    \
+    }
+#define SIMPLE_TOKEN2(ch, ch2, token, token2) \
+    else if(c == ch) {                        \
+        s++;                                  \
+        if(*s == ch2) {                       \
+            s++;                              \
+            tAddToken(t, token2);             \
+        } else {                              \
+            tAddToken(t, token);              \
+        }                                     \
+    }
+
 static void tParseLineString(TState* t, const char* s) {
     while(true) {
         char c = *s;
@@ -144,25 +158,35 @@ static void tParseLineString(TState* t, const char* s) {
         } else if(c == '"') {
             s = tokenizerAddString(t, s);
         } else if(c == '\n') {
-            tAddToken(t, NEWLINE);
+            tAddToken(t, TT_NEWLINE);
             break;
         } else if(c == ' ') {
             s++;
-        } else if(c == '+') {
-            tAddToken(t, PLUS);
-            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') {
+        }
+        SIMPLE_TOKEN(',', TT_COMMA)
+        SIMPLE_TOKEN('$', TT_DOLLAR)
+        SIMPLE_TOKEN('+', TT_ADD)
+        SIMPLE_TOKEN('-', TT_SUBTRACT)
+        SIMPLE_TOKEN('*', TT_MULTIPLY)
+        SIMPLE_TOKEN('/', TT_DIVIDE)
+        SIMPLE_TOKEN2('=', '=', TT_ASSIGN, TT_EQUAL)
+        SIMPLE_TOKEN2('!', '=', TT_NOT, TT_NOT_EQUAL)
+        SIMPLE_TOKEN2('>', '=', TT_GREATER, TT_GREATER_OR_EQUAL)
+        SIMPLE_TOKEN2('<', '=', TT_SMALLER, TT_SMALLER_OR_EQUAL)
+        SIMPLE_TOKEN('(', TT_OPEN_ROUND_BRACKET)
+        SIMPLE_TOKEN(')', TT_CLOSE_ROUND_BRACKET)
+        else if(c == '&' && s[1] == '&') {
+            s += 2;
+            tAddToken(t, TT_AND);
+        }
+        else if(c == '|' && s[1] == '|') {
+            s += 2;
+            tAddToken(t, TT_OR);
+        }
+        else if(c == '\0') {
             break;
-        } else {
+        }
+        else {
             tInvalidToken(t, c);
         }
     }
@@ -198,14 +222,14 @@ static i32 tReadInt32(Tokenizer* t) {
 }
 
 Token tokenizerNext(Tokenizer* t) {
-    Token token = {.type = INVALID};
+    Token token = {.type = TT_INVALID};
     if(bufferReadU8(&t->buffer, &token.type)) {
         return token;
     }
     switch(token.type) {
-        case STRING:
-        case LITERAL: token.stringValue = tReadString(t); break;
-        case INT32: token.intValue = tReadInt32(t); break;
+        case TT_STRING:
+        case TT_LITERAL: token.stringValue = tReadString(t); break;
+        case TT_INT32: token.intValue = tReadInt32(t); break;
         default: break;
     }
     return token;
@@ -248,20 +272,33 @@ void tokenizerDestroy(Tokenizer* t) {
 
 void tokenizerPrintToken(const Token* token, char* buffer, size_t n) {
     switch(token->type) {
-        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(IF, "If");
-        TOKEN_PRINTER(THEN, "Then");
-        TOKEN_PRINTER(ENDIF, "EndIf");
-        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");
+        TOKEN_PRINTER(TT_LITERAL, "Literal(%s)", token->stringValue);
+        TOKEN_PRINTER(TT_INT32, "Int32(%d)", token->intValue);
+        TOKEN_PRINTER(TT_STRING, "String(%s)", token->stringValue);
+        TOKEN_PRINTER(TT_DOLLAR, "$");
+        TOKEN_PRINTER(TT_COMMA, ",");
+        TOKEN_PRINTER(TT_ADD, "+");
+        TOKEN_PRINTER(TT_SUBTRACT, "-");
+        TOKEN_PRINTER(TT_MULTIPLY, "*");
+        TOKEN_PRINTER(TT_DIVIDE, "/");
+        TOKEN_PRINTER(TT_ASSIGN, "=");
+        TOKEN_PRINTER(TT_AND, "&&");
+        TOKEN_PRINTER(TT_OR, "||");
+        TOKEN_PRINTER(TT_NOT, "!");
+        TOKEN_PRINTER(TT_EQUAL, "==");
+        TOKEN_PRINTER(TT_NOT_EQUAL, "!=");
+        TOKEN_PRINTER(TT_GREATER, ">");
+        TOKEN_PRINTER(TT_SMALLER, "<");
+        TOKEN_PRINTER(TT_GREATER_OR_EQUAL, ">=");
+        TOKEN_PRINTER(TT_SMALLER_OR_EQUAL, "<=");
+        TOKEN_PRINTER(TT_IF, "If");
+        TOKEN_PRINTER(TT_FUNCTION, "Function");
+        TOKEN_PRINTER(TT_END, "End");
+        TOKEN_PRINTER(TT_RETURN, "Return");
+        TOKEN_PRINTER(TT_OPEN_ROUND_BRACKET, "(");
+        TOKEN_PRINTER(TT_CLOSE_ROUND_BRACKET, ")");
+        TOKEN_PRINTER(TT_NEWLINE, "Newline");
+        TOKEN_PRINTER(TT_INVALID, "Invalid");
     }
     snprintf(buffer, n, "Unknown");
 }

+ 27 - 14
src/Tokenizer.h

@@ -5,20 +5,33 @@
 #include "Error.h"
 
 typedef enum : u8 {
-    LITERAL,
-    INT32,
-    STRING,
-    PLUS,
-    EQUAL,
-    IF,
-    THEN,
-    ENDIF,
-    FUNCTION,
-    END,
-    OPEN_ROUND_BRACKET,
-    CLOSE_ROUND_BRACKET,
-    NEWLINE,
-    INVALID
+    TT_LITERAL,
+    TT_INT32,
+    TT_STRING,
+    TT_COMMA,
+    TT_DOLLAR,
+    TT_ADD,
+    TT_SUBTRACT,
+    TT_MULTIPLY,
+    TT_DIVIDE,
+    TT_ASSIGN,
+    TT_AND,
+    TT_OR,
+    TT_NOT,
+    TT_EQUAL,
+    TT_NOT_EQUAL,
+    TT_GREATER,
+    TT_SMALLER,
+    TT_GREATER_OR_EQUAL,
+    TT_SMALLER_OR_EQUAL,
+    TT_IF,
+    TT_FUNCTION,
+    TT_END,
+    TT_RETURN,
+    TT_OPEN_ROUND_BRACKET,
+    TT_CLOSE_ROUND_BRACKET,
+    TT_NEWLINE,
+    TT_INVALID
 } TokenType;
 
 typedef struct {

+ 4 - 0
test/Add.basic

@@ -1,3 +1,7 @@
 print 1 + 20
 print 1 + 20 + 4
 print 1 + 20 + 4 + 6
+print 5 - 6
+print 5 * 6
+print 16 / 5
+print 1 + 2 * 3

+ 4 - 0
test/Add.basic_result

@@ -1,3 +1,7 @@
 21
 25
 31
+-1
+30
+3
+7

+ 23 - 0
test/Condition.basic

@@ -0,0 +1,23 @@
+print 3 < 5
+print 5 < 3
+print 3 < 3
+
+print 3 > 5
+print 5 > 3
+print 3 > 3
+
+print 3 == 5
+print 5 == 3
+print 3 == 3
+
+print 3 != 5
+print 5 != 3
+print 3 != 3
+
+print 3 <= 5
+print 5 <= 3
+print 3 <= 3
+
+print 3 >= 5
+print 5 >= 3
+print 3 >= 3

+ 18 - 0
test/Condition.basic_result

@@ -0,0 +1,18 @@
+1
+0
+0
+0
+1
+0
+0
+0
+1
+1
+1
+0
+1
+0
+1
+0
+1
+1

+ 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

+ 26 - 0
test/Function3.basic

@@ -0,0 +1,26 @@
+c = 3
+d = 5
+
+function wusi(a, b)
+    d = 10
+    return a + b + $c + d
+end
+
+wusi(5, 6)
+print wusi(3, 4)
+print wusi(5, 4)
+
+function fac(n)
+    if n == 0
+        return 1
+    end
+    return n * fac(n - 1)
+end
+
+function baum()
+end
+
+print fac(4)
+print fac(5)
+print baum()
+print baum()

+ 6 - 0
test/Function3.basic_result

@@ -0,0 +1,6 @@
+20
+22
+24
+120
+0
+0

+ 12 - 12
test/If.basic

@@ -1,22 +1,22 @@
-if 1 then
+if 1
   print "wusi"
-  if 0 then
+  if 0
     print "wusi4"
-  endif
-  if 1 then
+  end
+  if 1
     print "wusi2"
-  endif
-endif
+  end
+end
 
-if 1 + 1 then
+if 1 + 1
   print "wusi3"
-endif
+end
 
-if 0 then
+if 0
   print "gusi"
-  if 1 then
+  if 1
     print "gusi2"
-  endif
-endif
+  end
+end
 
 print "end"

+ 31 - 0
test/Logic.basic

@@ -0,0 +1,31 @@
+print 0 && 0
+print 1 && 0
+print 0 && 1
+print 1 && 1
+
+print 0 || 0
+print 1 || 0
+print 0 || 1
+print 1 || 1
+
+print 0 && 0 && 0
+print 1 && 0 && 0
+print 0 && 1 && 0
+print 1 && 1 && 0
+print 0 && 0 && 1
+print 1 && 0 && 1
+print 0 && 1 && 1
+print 1 && 1 && 1
+
+print 0 || 0 || 0
+print 1 || 0 || 0
+print 0 || 1 || 0
+print 1 || 1 || 0
+print 0 || 0 || 1
+print 1 || 0 || 1
+print 0 || 1 || 1
+print 1 || 1 || 1
+
+print !3
+print !!3
+print !!!3

+ 27 - 0
test/Logic.basic_result

@@ -0,0 +1,27 @@
+0
+0
+0
+1
+0
+1
+1
+1
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+1
+1
+1
+1
+1
+1
+0
+1
+0