Browse Source

basic functions without a stack and arguments

Kajetan Johannes Hammerle 4 years ago
parent
commit
f209fbe331
10 changed files with 230 additions and 58 deletions
  1. 81 20
      Compiler.c
  2. 4 1
      Operation.h
  3. 56 36
      Script.c
  4. 41 0
      StringIntMap.c
  5. 18 0
      StringIntMap.h
  6. 7 0
      Tokenizer.c
  7. 3 0
      Tokenizer.h
  8. 9 1
      meson.build
  9. 7 0
      tests/functions/function
  10. 4 0
      tests/functions/function.out

+ 81 - 20
Compiler.c

@@ -5,6 +5,7 @@
 
 #include "Compiler.h"
 #include "Operation.h"
+#include "StringIntMap.h"
 #include "Tokenizer.h"
 
 #define MAX_BYTES (1024 * 1024)
@@ -17,9 +18,11 @@ static unsigned char byteCode[MAX_BYTES];
 static int writeIndex = 0;
 static int16 line = 1;
 
-static const char* vars[VARS];
+static StringIntMap vars;
 static int varIndex = 0;
 
+static StringIntMap functions;
+
 static void cError(const char* format, ...) {
     va_list args;
     va_start(args, format);
@@ -28,17 +31,10 @@ static void cError(const char* format, ...) {
 }
 
 static int cAddVar(const char* var) {
-    for(int i = 0; i < varIndex; i++) {
-        if(strcmp(var, vars[i]) == 0) {
-            return i;
-        }
-    }
-    if(varIndex >= VARS) {
-        cError("variable buffer is too small");
-        return -1;
-    }
     int index = varIndex;
-    vars[varIndex++] = var;
+    if(simAdd(&vars, var, &index)) {
+        varIndex++;
+    }
     return index;
 }
 
@@ -123,9 +119,6 @@ static bool cPrimary() {
             return false;
         }
         int varPointer = cAddVar(literal);
-        if(varPointer == -1) {
-            return false;
-        }
         return cAddOperation(OP_GET) && cAddBytes(&varPointer, sizeof(int));
     }
     cUnexpectedToken(tPeekToken());
@@ -160,6 +153,24 @@ static bool cExpression() {
     return cAdd();
 }
 
+static bool cSetVar(const char* literal) {
+    int varPointer = cAddVar(literal);
+    return cExpression() && cAddOperation(OP_SET) && cAddBytes(&varPointer, sizeof(int)) && cConsumeToken(T_SEMICOLON);
+}
+
+static bool cCallFunction(const char* literal) {
+    int index;
+    if(simSearch(&functions, literal, &index)) {
+        if(!cAddOperation(OP_GOSUB) || !cAddBytes(&index, sizeof(int))) {
+            return false;
+        }
+    } else {
+        cError("unknown function on line %d", line);
+        return false;
+    }
+    return cConsumeToken(T_CLOSE_BRACKET) && cConsumeToken(T_SEMICOLON);
+}
+
 static bool cLiteral() {
     const char* literal = tReadString();
     if(literal == NULL) {
@@ -167,15 +178,55 @@ static bool cLiteral() {
         return false;
     }
     Token t = tReadTokenAndLine();
-    if(t != T_SET) {
-        cUnexpectedToken(t);
+    if(t == T_SET) {
+        return cSetVar(literal);
+    } else if(t == T_OPEN_BRACKET) {
+        return cCallFunction(literal);
+    }
+    cUnexpectedToken(t);
+    return false;
+}
+
+static bool cLine();
+
+static bool cFunction() {
+    if(!cConsumeToken(T_LITERAL)) {
         return false;
     }
-    int varPointer = cAddVar(literal);
-    if(varPointer == -1) {
+    const char* name = tReadString();
+    if(name == NULL) {
+        cError("function literal without a function name on line %d", line);
         return false;
     }
-    return cExpression() && cAddOperation(OP_SET) && cAddBytes(&varPointer, sizeof(int)) && cConsumeToken(T_SEMICOLON);
+    if(!cConsumeToken(T_OPEN_BRACKET) || !cConsumeToken(T_CLOSE_BRACKET) || !cConsumeToken(T_OPEN_CURVED_BRACKET)) {
+        return false;
+    }
+
+    if(!cAddOperation(OP_GOTO)) {
+        return false;
+    }
+    void* p = cReserveBytes(sizeof(int));
+
+    int functionIndex = writeIndex;
+    if(!simAdd(&functions, name, &functionIndex)) {
+        cError("function registered twice on line %d", line);
+        return false;
+    }
+
+    int oldLine = line;
+    while(!cConsumeTokenIf(T_CLOSE_CURVED_BRACKET)) {
+        if(cConsumeTokenIf(T_END)) {
+            cError("unexpected end of file: function not closed on line %d", oldLine);
+            return false;
+        }
+        cLine();
+    }
+
+    if(!cAddOperation(OP_RETURN)) {
+        return false;
+    }
+    cSetBytes(&writeIndex, p, sizeof(int));
+    return true;
 }
 
 static bool cPrint() {
@@ -193,6 +244,8 @@ static bool cLine() {
         return cPrint();
     } else if(t == T_LITERAL) {
         return cLiteral();
+    } else if(t == T_FUNCTION) {
+        return cFunction();
     }
     cUnexpectedToken(t);
     return false;
@@ -217,9 +270,17 @@ static void cForEachLine() {
     cAddBytes(&varIndex, sizeof(int));
 }
 
+void cAllocAndCompile() {
+    simInit(&vars);
+    simInit(&functions);
+    cForEachLine();
+    simDelete(&functions);
+    simDelete(&vars);
+}
+
 unsigned char* cCompile(int* codeLength) {
     error[0] = '\0';
-    cForEachLine();
+    cAllocAndCompile();
     if(error[0] != '\0') {
         return NULL;
     }

+ 4 - 1
Operation.h

@@ -15,7 +15,10 @@ typedef enum Operation {
     OP_ADD,
     OP_MUL,
     OP_PRINT,
-    OP_LINE
+    OP_LINE,
+    OP_GOTO,
+    OP_GOSUB,
+    OP_RETURN
 } Operation;
 
 #endif

+ 56 - 36
Script.c

@@ -26,6 +26,7 @@ static ObjectPrinter printer = sPrinter;
 
 static bool sRead(Script* sc, void* buffer, int length) {
     if(sc->readIndex + length > sc->byteCodeLength) {
+        sError(sc, "cannot read expected %d bytes of data from bytecode on line %d", sc->line);
         return true;
     }
     memcpy(buffer, sc->byteCode + sc->readIndex, length);
@@ -37,19 +38,21 @@ static Operation sReadOperation(Script* sc) {
     unsigned char c;
     if(sRead(sc, &c, 1)) {
         return OP_NOTHING;
-    } /*else if(sRead(sc, &sc->line, 2)) {
-        sError(sc, "operation without line near line %d", sc->line);
-        return OP_NOTHING;
-    }*/
+    }
     return c;
 }
 
-static void sPush(Script* sc, Object* o) {
+static bool sReadInt(Script* sc, int* i) {
+    return !sRead(sc, i, sizeof(int));
+}
+
+static bool sPush(Script* sc, Object* o) {
     if(sc->stackIndex >= SCRIPT_STACK_SIZE) {
         sError(sc, "stack overflow on line %d", sc->line);
-        return;
+        return false;
     }
     sc->stack[sc->stackIndex++] = *o;
+    return true;
 }
 
 static bool sPop(Script* sc, Object* o) {
@@ -61,9 +64,9 @@ static bool sPop(Script* sc, Object* o) {
     return false;
 }
 
-static void sPushInt(Script* sc, int value) {
+static bool sPushInt(Script* sc, int value) {
     Object o = {.type = OT_INT, .data.intValue = value};
-    sPush(sc, &o);
+    return sPush(sc, &o);
 }
 
 static void sPushFloat(Script* sc, float value) {
@@ -83,51 +86,43 @@ static void sPushBool(Script* sc, bool value) {
 
 static void sPushVars(Script* sc) {
     int value = 0;
-    if(sRead(sc, &value, sizeof(int))) {
-        sError(sc, "cannot read stack frame size from the bytecode on line %d", sc->line);
-        return;
-    }
-    for(int i = 0; i < value; i++) {
-        sPushNull(sc);
+    if(sReadInt(sc, &value)) {
+        for(int i = 0; i < value; i++) {
+            sPushNull(sc);
+        }
     }
 }
 
 static void sPopVars(Script* sc) {
     int value = 0;
-    if(sRead(sc, &value, sizeof(int))) {
-        sError(sc, "cannot read stack frame size from the bytecode on line %d", sc->line);
-    } else if(sc->stackIndex < value) {
-        sError(sc, "stack underflow on line %d", sc->line);
-    } else {
-        sc->stackIndex -= value;
+    if(sReadInt(sc, &value)) {
+        if(sc->stackIndex < value) {
+            sError(sc, "stack underflow on line %d", sc->line);
+        } else {
+            sc->stackIndex -= value;
+        }
     }
 }
 
 static void sSet(Script* sc) {
     int value = 0;
-    if(sRead(sc, &value, sizeof(int))) {
-        sError(sc, "cannot read address of variable on line %d", sc->line);
-        return;
+    if(sReadInt(sc, &value)) {
+        sPop(sc, sc->stack + value);
     }
-    sPop(sc, sc->stack + value);
 }
 
 static void sGet(Script* sc) {
     int value = 0;
-    if(sRead(sc, &value, sizeof(int))) {
-        sError(sc, "cannot read address of variable on line %d", sc->line);
-        return;
+    if(sReadInt(sc, &value)) {
+        sPush(sc, sc->stack + value);
     }
-    sPush(sc, sc->stack + value);
 }
 
 static void sPushCodeInt(Script* sc) {
     int value = 0;
-    if(sRead(sc, &value, sizeof(int))) {
-        sError(sc, "cannot read an int from the bytecode on line %d", sc->line);
-        return;
+    if(sReadInt(sc, &value)) {
+        sPushInt(sc, value);
     }
-    sPushInt(sc, value);
 }
 
 static void sPushCodeFloat(Script* sc) {
@@ -184,10 +179,7 @@ static float sFloatMul(float a, float b) {
 
 static void sPrint(Script* sc) {
     Object o;
-    if(sPop(sc, &o)) {
-        return;
-    }
-    if(printer(&o)) {
+    if(!sPop(sc, &o) && printer(&o)) {
         sError(sc, "cannot print given object on line %d", sc->line);
     }
 }
@@ -198,6 +190,31 @@ static void sLine(Script* sc) {
     }
 }
 
+static void sGoTo(Script* sc) {
+    int gotoIndex;
+    if(sReadInt(sc, &gotoIndex)) {
+        sc->readIndex = gotoIndex;
+    }
+}
+
+static void sGoSub(Script* sc) {
+    int gotoIndex;
+    if(sReadInt(sc, &gotoIndex) && sPushInt(sc, sc->readIndex)) {
+        sc->readIndex = gotoIndex;
+    }
+}
+
+static void sReturn(Script* sc) {
+    Object o;
+    if(sPop(sc, &o)) {
+        return;
+    } else if(o.type != OT_INT) {
+        sError(sc, "return address on stack is not an int");
+        return;
+    }
+    sc->readIndex = o.data.intValue;
+}
+
 static void sConsumeInstruction(Script* sc) {
     switch(sReadOperation(sc)) {
         case OP_NOTHING: break;
@@ -214,6 +231,9 @@ static void sConsumeInstruction(Script* sc) {
         case OP_MUL: sIntBinary(sc, sIntMul, sFloatMul); break;
         case OP_PRINT: sPrint(sc); break;
         case OP_LINE: sLine(sc); break;
+        case OP_GOTO: sGoTo(sc); break;
+        case OP_GOSUB: sGoSub(sc); break;
+        case OP_RETURN: sReturn(sc); break;
     }
 }
 

+ 41 - 0
StringIntMap.c

@@ -0,0 +1,41 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "StringIntMap.h"
+
+void simInit(StringIntMap* sim) {
+    sim->capacity = 16;
+    sim->entries = 0;
+    sim->stringData = malloc(sizeof(const char*) * sim->capacity);
+    sim->intData = malloc(sizeof(int) * sim->capacity);
+}
+
+void simDelete(StringIntMap* sim) {
+    free(sim->stringData);
+    free(sim->intData);
+}
+
+bool simSearch(StringIntMap* sim, const char* s, int* mapping) {
+    for(int i = 0; i < sim->entries; i++) {
+        if(strcmp(s, sim->stringData[i]) == 0) {
+            *mapping = sim->intData[i];
+            return true;
+        }
+    }
+    return false;
+}
+
+bool simAdd(StringIntMap* sim, const char* s, int* mapping) {
+    if(simSearch(sim, s, mapping)) {
+        return false;
+    } else if(sim->entries >= sim->capacity) {
+        sim->capacity *= 2;
+        sim->stringData = realloc(sim->stringData, sizeof(const char*) * sim->capacity);
+        sim->intData = realloc(sim->intData, sizeof(int) * sim->capacity);
+    }
+    int index = sim->entries++;
+    sim->stringData[index] = s;
+    sim->intData[index] = *mapping;
+    return true;
+}

+ 18 - 0
StringIntMap.h

@@ -0,0 +1,18 @@
+#ifndef STRINGINTMAP_H
+#define STRINGINTMAP_H
+
+#include <stdbool.h>
+
+typedef struct StringIntMap {
+    int capacity;
+    int entries;
+    const char** stringData;
+    int* intData;
+} StringIntMap;
+
+void simInit(StringIntMap* sim);
+void simDelete(StringIntMap* sim);
+bool simSearch(StringIntMap* sim, const char* s, int* mapping);
+bool simAdd(StringIntMap* sim, const char* s, int* mapping);
+
+#endif

+ 7 - 0
Tokenizer.c

@@ -78,6 +78,8 @@ static bool tParseLiteral(int c) {
         return tAddToken(T_TRUE);
     } else if(strcmp(buffer, "false") == 0) {
         return tAddToken(T_FALSE);
+    } else if(strcmp(buffer, "function") == 0) {
+        return tAddToken(T_FUNCTION);
     }
     return tAddToken(T_LITERAL) && tAdd(buffer, index + 1);
 }
@@ -138,6 +140,8 @@ static bool tParseToken() {
         case ';': return tAddToken(T_SEMICOLON);
         case '(': return tAddToken(T_OPEN_BRACKET);
         case ')': return tAddToken(T_CLOSE_BRACKET);
+        case '{': return tAddToken(T_OPEN_CURVED_BRACKET);
+        case '}': return tAddToken(T_CLOSE_CURVED_BRACKET);
     }
     tError("unknown character on line %d: %c", line, c);
     return false;
@@ -230,9 +234,12 @@ const char* tGetTokenName(Token token) {
         case T_SET: return "=";
         case T_LITERAL: return "literal";
         case T_PRINT: return "print";
+        case T_FUNCTION: return "function";
         case T_SEMICOLON: return ";";
         case T_OPEN_BRACKET: return "(";
         case T_CLOSE_BRACKET: return ")";
+        case T_OPEN_CURVED_BRACKET: return "{";
+        case T_CLOSE_CURVED_BRACKET: return "}";
         case T_END: return "end";
     }
     return "Unknown";

+ 3 - 0
Tokenizer.h

@@ -14,9 +14,12 @@ typedef enum Token {
     T_SET,
     T_LITERAL,
     T_PRINT,
+    T_FUNCTION,
     T_SEMICOLON,
     T_OPEN_BRACKET,
     T_CLOSE_BRACKET,
+    T_OPEN_CURVED_BRACKET,
+    T_CLOSE_CURVED_BRACKET,
     T_END
 } Token;
 

+ 9 - 1
meson.build

@@ -1,6 +1,14 @@
 project('lonely tiger', 'c')
 
-src = ['Main.c', 'Tokenizer.c', 'Compiler.c', 'Utils.c', 'Script.c', 'Test.c']
+src = [
+    'Main.c', 
+    'Tokenizer.c', 
+    'Compiler.c', 
+    'Utils.c', 
+    'Script.c', 
+    'Test.c', 
+    'StringIntMap.c'
+]
 
 executable('lonely_tiger', 
     sources: src,

+ 7 - 0
tests/functions/function

@@ -0,0 +1,7 @@
+function test() {
+    print 4;
+    print 5;
+}
+
+test();
+test();

+ 4 - 0
tests/functions/function.out

@@ -0,0 +1,4 @@
+4
+5
+4
+5