Răsfoiți Sursa

Functions with return value, improved checking

Kajetan Johannes Hammerle 1 săptămână în urmă
părinte
comite
e548287bdb
10 a modificat fișierele cu 314 adăugiri și 117 ștergeri
  1. 35 3
      src/Code.c
  2. 4 1
      src/Code.h
  3. 159 56
      src/Compiler.c
  4. 6 6
      src/Main.c
  5. 50 35
      src/Tokenizer.c
  6. 20 16
      src/Tokenizer.h
  7. 4 0
      test/Add.basic
  8. 4 0
      test/Add.basic_result
  9. 26 0
      test/Function3.basic
  10. 6 0
      test/Function3.basic_result

+ 35 - 3
src/Code.c

@@ -85,7 +85,25 @@ 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 iPushConstantString(Code* c) {
@@ -176,6 +194,14 @@ static bool iReturn(Code* c) {
     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;
@@ -233,6 +259,9 @@ 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_INT32: return iPushInt(c);
         case PRINT: return iPrint(c);
@@ -240,7 +269,7 @@ static bool execute(Code* c, Instruction command) {
         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);
@@ -295,13 +324,16 @@ void codeDump(const Code* code) {
         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);
+            DUMP_INT(RETURN_VALUE);
             DUMP_INT(READ_VARIABLE);
             DUMP_INT(SET_VARIABLE);
             DUMP_INT(PUSH_STACK_VARIABLES);

+ 4 - 1
src/Code.h

@@ -19,6 +19,9 @@ static_assert(sizeof(Value) == 8);
 
 typedef enum : u8 {
     ADD,
+    SUB,
+    MUL,
+    DIV,
     PUSH_CONSTANT_STRING,
     PUSH_INT32,
     PRINT,
@@ -26,7 +29,7 @@ typedef enum : u8 {
     JUMP,
     JUMP_ON_0,
     JUMP_SUB,
-    RETURN,
+    RETURN_VALUE,
     READ_VARIABLE,
     SET_VARIABLE,
     PUSH_STACK_VARIABLES,

+ 159 - 56
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;
 
@@ -105,7 +115,8 @@ static i32 addVariable(Context* c, const char* name, bool forceGlobal) {
 }
 
 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) {
@@ -114,30 +125,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;
 }
 
@@ -149,6 +168,30 @@ static void checkForInvalidFunctions(Context* c) {
     }
 }
 
+static void addReturn(Context* c, i32 address, bool hasArgument) {
+    if(c->returns != nullptr && c->returns->hasArgument != hasArgument) {
+        THROW_ERROR("Inconsistent returns in function");
+    }
+    Return* r = memoryAllocate(sizeof(Return));
+    if(r == nullptr) {
+        THROW_ERROR("Too less memory for returns");
+    }
+    r->next = c->returns;
+    r->hasArgument = hasArgument;
+    r->address = address;
+    c->returns = r;
+}
+
+static void cleanReturns(Context* c) {
+    Return* r = c->returns;
+    while(r != nullptr) {
+        Return* next = r->next;
+        memoryFree(r);
+        r = next;
+    }
+    c->returns = nullptr;
+}
+
 static void cleanContext(Context* c) {
     Variable* v = c->variables;
     while(v != nullptr) {
@@ -165,26 +208,30 @@ static void cleanContext(Context* c) {
         f = next;
     }
     c->functions = nullptr;
+
+    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 void consumeNewline(Context* c) {
+    if(tokenizerPeek(c->tokenizer).type == TT_INVALID) {
+        return;
+    }
+    consumeToken(c, TT_NEWLINE);
     c->line++;
-    return t;
 }
 
 static void compileReadVariable(Context* c, Token token, bool forceGlobal) {
@@ -193,30 +240,63 @@ static void compileReadVariable(Context* c, Token token, bool forceGlobal) {
     codePushI32(c, 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) {
+    } else if(token.type == TT_INT32) {
         codePushInstruction(c, PUSH_INT32);
         codePushI32(c, token.intValue);
-    } else if(token.type == LITERAL) {
-        compileReadVariable(c, token, false);
-    } else if(token.type == DOLLAR) {
-        token = consumeToken(c, LITERAL);
+    } else if(token.type == TT_LITERAL) {
+        if(tokenizerPeek(c->tokenizer).type == 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) {
+static void compileMul(Context* c) {
     compileConstant(c);
-    while(tokenizerPeek(c->tokenizer).type == PLUS) {
-        tokenizerNext(c->tokenizer);
-        compileConstant(c);
-        codePushInstruction(c, ADD);
+    while(true) {
+        Token t = tokenizerPeek(c->tokenizer);
+        if(t.type == TT_ASTERISK) {
+            tokenizerNext(c->tokenizer);
+            compileMul(c);
+            codePushInstruction(c, MUL);
+        } else if(t.type == TT_SLASH) {
+            tokenizerNext(c->tokenizer);
+            compileMul(c);
+            codePushInstruction(c, DIV);
+        } else {
+            break;
+        }
+    }
+}
+
+static void compileAdd(Context* c) {
+    compileMul(c);
+    while(true) {
+        Token t = tokenizerPeek(c->tokenizer);
+        if(t.type == TT_PLUS) {
+            tokenizerNext(c->tokenizer);
+            compileMul(c);
+            codePushInstruction(c, ADD);
+        } else if(t.type == TT_MINUS) {
+            tokenizerNext(c->tokenizer);
+            compileMul(c);
+            codePushInstruction(c, SUB);
+        } else {
+            break;
+        }
     }
 }
 
@@ -231,21 +311,21 @@ static void compileIf(Context* c) {
     codePushInstruction(c, JUMP_ON_0);
     size_t posIndex = codeGetWritePosition(c);
     codePushI32(c, 0);
-    consumeToken(c, THEN);
+    consumeToken(c, TT_THEN);
     consumeNewline(c);
     while(true) {
-        if(tokenizerPeek(c->tokenizer).type == ENDIF) {
+        if(tokenizerPeek(c->tokenizer).type == TT_ENDIF) {
             break;
         }
         compileLine(c, tokenizerNext(c->tokenizer));
     }
-    consumeToken(c, ENDIF);
+    consumeToken(c, TT_ENDIF);
     consumeNewline(c);
     codeRewriteI32(c, posIndex, (i32)codeGetWritePosition(c));
 }
 
 static void compileSetVariable(Context* c, const char* name, bool forceGlobal) {
-    consumeToken(c, EQUAL);
+    consumeToken(c, TT_EQUAL);
     compileExpression(c);
     i32 index = addVariable(c, name, forceGlobal);
     codePushInstruction(c, SET_VARIABLE);
@@ -262,38 +342,45 @@ static void compileFunction(Context* c) {
     size_t pos = codeGetWritePosition(c);
     codePushI32(c, 0);
 
-    Token t = consumeToken(c, LITERAL);
-    addFunction(c, t.stringValue, (i32)codeGetWritePosition(c));
+    i32 functionStart = (i32)codeGetWritePosition(c);
+    Token t = consumeToken(c, TT_LITERAL);
 
     codePushInstruction(c, PUSH_STACK_VARIABLES);
     size_t stackVarsLoc = codeGetWritePosition(c);
     codePushI32(c, 0);
 
-    consumeToken(c, OPEN_ROUND_BRACKET);
+    consumeToken(c, TT_OPEN_ROUND_BRACKET);
     i32 vars = 0;
     Variable* resetVar = c->variables;
     while(true) {
-        if(tokenizerPeek(c->tokenizer).type != LITERAL) {
+        if(tokenizerPeek(c->tokenizer).type != TT_LITERAL) {
             break;
         }
-        Token varToken = consumeToken(c, LITERAL);
+        Token varToken = consumeToken(c, TT_LITERAL);
         addVariable(c, varToken.stringValue, false);
         vars++;
-        if(tokenizerPeek(c->tokenizer).type == COMMA) {
-            consumeToken(c, COMMA);
+        if(tokenizerPeek(c->tokenizer).type == TT_COMMA) {
+            consumeToken(c, TT_COMMA);
         }
     }
-    consumeToken(c, CLOSE_ROUND_BRACKET);
+    addFunction(c, t.stringValue, functionStart, vars);
+    consumeToken(c, TT_CLOSE_ROUND_BRACKET);
     while(true) {
-        if(tokenizerPeek(c->tokenizer).type == END) {
+        if(tokenizerPeek(c->tokenizer).type == TT_END) {
             break;
         }
         compileLine(c, tokenizerNext(c->tokenizer));
     }
-    consumeToken(c, END);
+    consumeToken(c, TT_END);
     i32 popVars = hasLocalVar(c) ? -c->variables->index : 0;
-    codePushInstruction(c, RETURN);
+    codePushInstruction(c, PUSH_INT32);
+    codePushI32(c, 0);
+    codePushInstruction(c, RETURN_VALUE);
     codePushI32(c, popVars);
+    for(Return* r = c->returns; r != nullptr; r = r->next) {
+        codeRewriteI32(c, (size_t)r->address, popVars);
+    }
+    cleanReturns(c);
     codeRewriteI32(c, stackVarsLoc, popVars - vars);
 
     while(c->variables != resetVar) {
@@ -313,55 +400,71 @@ static void compileCallFunction(Context* c, Token t) {
     codePushInstruction(c, PUSH_INT32);
     codePushI32(c, 0);
 
-    consumeToken(c, OPEN_ROUND_BRACKET);
+    consumeToken(c, TT_OPEN_ROUND_BRACKET);
     i32 offset = 0;
     while(true) {
-        if(tokenizerPeek(c->tokenizer).type == CLOSE_ROUND_BRACKET) {
+        if(tokenizerPeek(c->tokenizer).type == TT_CLOSE_ROUND_BRACKET) {
             break;
         }
         offset++;
         compileExpression(c);
-        if(tokenizerPeek(c->tokenizer).type == COMMA) {
-            consumeToken(c, COMMA);
+        if(tokenizerPeek(c->tokenizer).type == TT_COMMA) {
+            consumeToken(c, TT_COMMA);
         }
     }
-    consumeToken(c, CLOSE_ROUND_BRACKET);
+    consumeToken(c, TT_CLOSE_ROUND_BRACKET);
 
     codePushInstruction(c, JUMP_SUB);
     size_t pos = codeGetWritePosition(c);
-    codePushI32(c, addCallFunction(c, t.stringValue, (i32)pos));
+    codePushI32(c, addCallFunction(c, t.stringValue, (i32)pos, offset));
     codePushI32(c, offset);
-    consumeNewline(c);
+}
+
+static void compileReturn(Context* c) {
+    if(tokenizerPeek(c->tokenizer).type == TT_NEWLINE) {
+        consumeNewline(c);
+        codePushInstruction(c, PUSH_INT32);
+        codePushI32(c, 0);
+    } else {
+        compileExpression(c);
+    }
+    codePushInstruction(c, RETURN_VALUE);
+    i32 address = (i32)codeGetWritePosition(c);
+    codePushI32(c, 0);
+    addReturn(c, address, true);
 }
 
 static void compileLine(Context* c, Token token) {
-    if(token.type == NEWLINE) {
+    if(token.type == TT_NEWLINE) {
         c->line++;
         return;
-    } else if(token.type == IF) {
+    } else if(token.type == TT_IF) {
         compileIf(c);
         return;
-    } else if(token.type == FUNCTION) {
+    } else if(token.type == TT_FUNCTION) {
         compileFunction(c);
         return;
-    } else if(token.type == DOLLAR) {
-        token = consumeToken(c, LITERAL);
+    } else if(token.type == TT_RETURN) {
+        compileReturn(c);
+        return;
+    } else if(token.type == TT_DOLLAR) {
+        token = consumeToken(c, TT_LITERAL);
         compileSetVariable(c, token.stringValue, true);
         return;
-    } else if(token.type != LITERAL) {
-        unexpectedToken(c, token);
+    } else if(token.type != TT_LITERAL) {
+        unexpectedToken(c, token, __LINE__);
     }
     const char* s = token.stringValue;
     if(strcmp(s, "print") == 0) {
-        while(tokenizerPeek(c->tokenizer).type != NEWLINE) {
+        while(tokenizerPeek(c->tokenizer).type != TT_NEWLINE) {
             compileExpression(c);
             codePushInstruction(c, PRINT);
         }
         consumeNewline(c);
         codePushInstruction(c, PRINT_NEWLINE);
-    } else if(tokenizerPeek(c->tokenizer).type == EQUAL) {
+    } else if(tokenizerPeek(c->tokenizer).type == TT_EQUAL) {
         compileSetVariable(c, s, false);
-    } else if(tokenizerPeek(c->tokenizer).type == OPEN_ROUND_BRACKET) {
+    } else if(tokenizerPeek(c->tokenizer).type == TT_OPEN_ROUND_BRACKET) {
         compileCallFunction(c, token);
     } else {
         THROW_ERROR("Unexpected literal(%s)", s);
@@ -376,7 +479,7 @@ static void parseTokens(Context* c) {
 
     while(true) {
         Token token = tokenizerNext(c->tokenizer);
-        if(token.type == INVALID) {
+        if(token.type == TT_INVALID) {
             break;
         }
         compileLine(c, token);

+ 6 - 6
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]);
@@ -31,15 +31,15 @@ int main(int argCount, const char** args) {
         puts(e.text);
     } else {
         // codeDump(&code);
-        // return 0;
-        memoryDump();
+        //   return 0;
+        // memoryDump();
         codeRun(&code);
         if(codeHasRunError(&code)) {
             puts(codeGetRunError(&code));
         }
-        memoryDump();
+        // memoryDump();
     }
     codeDestroy(&code);
-    memoryDump();
+    // memoryDump();
     return 0;
 }

+ 50 - 35
src/Tokenizer.c

@@ -68,17 +68,19 @@ static const char* tokenizerAddLiteral(TState* t, const char* s) {
         buffer[index++] = c;
     }
     if(strcmp(buffer, "if") == 0) {
-        tAddToken(t, IF);
+        tAddToken(t, TT_IF);
     } else if(strcmp(buffer, "then") == 0) {
-        tAddToken(t, THEN);
+        tAddToken(t, TT_THEN);
     } else if(strcmp(buffer, "endif") == 0) {
-        tAddToken(t, ENDIF);
+        tAddToken(t, TT_ENDIF);
     } 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 +107,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 +115,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) {
@@ -144,27 +146,36 @@ 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, COMMA);
+            tAddToken(t, TT_COMMA);
             s++;
         } else if(c == '$') {
-            tAddToken(t, DOLLAR);
+            tAddToken(t, TT_DOLLAR);
             s++;
         } else if(c == '+') {
-            tAddToken(t, PLUS);
+            tAddToken(t, TT_PLUS);
+            s++;
+        } else if(c == '-') {
+            tAddToken(t, TT_MINUS);
+            s++;
+        } else if(c == '*') {
+            tAddToken(t, TT_ASTERISK);
+            s++;
+        } else if(c == '/') {
+            tAddToken(t, TT_SLASH);
             s++;
         } else if(c == '=') {
-            tAddToken(t, EQUAL);
+            tAddToken(t, TT_EQUAL);
             s++;
         } else if(c == '(') {
-            tAddToken(t, OPEN_ROUND_BRACKET);
+            tAddToken(t, TT_OPEN_ROUND_BRACKET);
             s++;
         } else if(c == ')') {
-            tAddToken(t, CLOSE_ROUND_BRACKET);
+            tAddToken(t, TT_CLOSE_ROUND_BRACKET);
             s++;
         } else if(c == '\0') {
             break;
@@ -204,14 +215,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;
@@ -254,22 +265,26 @@ 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(DOLLAR, "$");
-        TOKEN_PRINTER(COMMA, ",");
-        TOKEN_PRINTER(PLUS, "+");
-        TOKEN_PRINTER(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_PLUS, "+");
+        TOKEN_PRINTER(TT_MINUS, "-");
+        TOKEN_PRINTER(TT_ASTERISK, "*");
+        TOKEN_PRINTER(TT_SLASH, "/");
+        TOKEN_PRINTER(TT_EQUAL, "=");
+        TOKEN_PRINTER(TT_IF, "If");
+        TOKEN_PRINTER(TT_THEN, "Then");
+        TOKEN_PRINTER(TT_ENDIF, "EndIf");
+        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");
 }

+ 20 - 16
src/Tokenizer.h

@@ -5,22 +5,26 @@
 #include "Error.h"
 
 typedef enum : u8 {
-    LITERAL,
-    INT32,
-    STRING,
-    COMMA,
-    DOLLAR,
-    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_PLUS,
+    TT_MINUS,
+    TT_ASTERISK,
+    TT_SLASH,
+    TT_EQUAL,
+    TT_IF,
+    TT_THEN,
+    TT_ENDIF,
+    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

+ 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 then
+        return n * fac(n - 1)
+    endif
+    return 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