Browse Source

if, less and subtraction, recursion tests

Kajetan Johannes Hammerle 4 years ago
parent
commit
2f86004ede
10 changed files with 143 additions and 23 deletions
  1. 43 10
      Compiler.c
  2. 3 0
      Operation.h
  3. 49 0
      Script.c
  4. 1 1
      Script.h
  5. 18 12
      Tokenizer.c
  6. 3 0
      Tokenizer.h
  7. 10 0
      tests/functions/recursion
  8. 3 0
      tests/functions/recursion.out
  9. 9 0
      tests/if/if
  10. 4 0
      tests/if/if.out

+ 43 - 10
Compiler.c

@@ -208,14 +208,29 @@ static void cMul() {
 
 static void cAdd() {
     cMul();
-    while(cConsumeTokenIf(T_ADD)) {
-        cMul();
-        cAddOperation(OP_ADD);
+    while(true) {
+        if(cConsumeTokenIf(T_ADD)) {
+            cMul();
+            cAddOperation(OP_ADD);
+        } else if(cConsumeTokenIf(T_SUB)) {
+            cMul();
+            cAddOperation(OP_SUB);
+        } else {
+            break;
+        }
     }
 }
 
-static void cExpression() {
+static void cComparison() {
     cAdd();
+    while(cConsumeTokenIf(T_LESS)) {
+        cAdd();
+        cAddOperation(OP_LESS);
+    }
+}
+
+static void cExpression() {
+    cComparison();
 }
 
 static void cSetVar(const char* literal) {
@@ -253,17 +268,19 @@ static int cFunctionArguments() {
 
 static void cLine(Token t);
 
-static void cFunctionInnerBody(int arguments) {
-    int p = cAddPush(arguments);
+static void cConsumeBody() {
+    cConsumeToken(T_OPEN_CURVED_BRACKET);
     int oldLine = line;
     while(!cConsumeTokenIf(T_CLOSE_CURVED_BRACKET)) {
         Token t = cReadTokenAndLine();
         if(t == T_END) {
-            cError("unexpected end of file: function not closed on line %d", oldLine);
+            cError("unexpected end of file: non closed curved bracket on line %d", oldLine);
         }
         cLine(t);
     }
-    cAddPop(p, vars[1].entries);
+}
+
+static void cLinkReturns() {
     for(int i = 0; i < returnIndex; i++) {
         cSetInt(returns[i], vars[1].entries);
     }
@@ -272,13 +289,18 @@ static void cFunctionInnerBody(int arguments) {
 
 static void cFunctionBody(const char* name, int arguments) {
     int oldLine = line;
-    cConsumeToken(T_OPEN_CURVED_BRACKET);
     cAddOperation(OP_GOTO);
     int gotoIndex = cReserveInt();
 
     int address = code->length;
     returnState = 0;
-    cFunctionInnerBody(arguments);
+
+    int p = cAddPush(arguments);
+    cConsumeBody();
+    cAddPop(p, vars[1].entries);
+
+    cLinkReturns();
+
     if(!fmAdd(&functions, name, arguments, address, returnState == 2)) {
         cError("function registered twice on line %d", oldLine);
     }
@@ -336,6 +358,16 @@ static void cPrint() {
     cAddOperation(OP_PRINT);
 }
 
+static void cIf() {
+    cConsumeToken(T_OPEN_BRACKET);
+    cExpression();
+    cConsumeToken(T_CLOSE_BRACKET);
+    cAddOperation(OP_IF_GOTO);
+    int p = cReserveInt();
+    cConsumeBody();
+    cSetInt(p, code->length);
+}
+
 static void cLine(Token t) {
     cAddOperation(OP_LINE);
     cAddInt16(line);
@@ -344,6 +376,7 @@ static void cLine(Token t) {
         case T_LITERAL: cLineLiteral(); break;
         case T_FUNCTION: cFunction(); break;
         case T_RETURN: cReturn(); break;
+        case T_IF: cIf(); break;
         default: cUnexpectedToken(t);
     }
 }

+ 3 - 0
Operation.h

@@ -14,11 +14,14 @@ typedef enum Operation {
     OP_SET,
     OP_GET,
     OP_ADD,
+    OP_SUB,
     OP_MUL,
+    OP_LESS,
     OP_PRINT,
     OP_LINE,
     OP_GOTO,
     OP_GOSUB,
+    OP_IF_GOTO,
     OP_SET_RETURN,
     OP_RETURN
 } Operation;

+ 49 - 0
Script.c

@@ -183,6 +183,10 @@ static int sIntAdd(int a, int b) {
     return a + b;
 }
 
+static int sIntSub(int a, int b) {
+    return b - a;
+}
+
 static int sIntMul(int a, int b) {
     return a * b;
 }
@@ -191,10 +195,37 @@ static float sFloatAdd(float a, float b) {
     return a + b;
 }
 
+static float sFloatSub(float a, float b) {
+    return b - a;
+}
+
 static float sFloatMul(float a, float b) {
     return a * b;
 }
 
+static void sBoolBinary(Script* sc, bool (*fInt)(int, int), bool (*fFloat)(float, float)) {
+    Object o[2];
+    if(sPop(sc, o) || sPop(sc, o + 1)) {
+        return;
+    }
+    if(o[0].type == OT_INT && o[1].type == OT_INT) {
+        sPushBool(sc, fInt(o[0].data.intValue, o[1].data.intValue));
+        return;
+    }
+    float f[2];
+    if(sToFloat(sc, o, f) && sToFloat(sc, o + 1, f + 1)) {
+        sPushBool(sc, fFloat(f[0], f[1]));
+    }
+}
+
+static bool sIntLess(int a, int b) {
+    return b < a;
+}
+
+static bool sFloatLess(float a, float b) {
+    return b < a;
+}
+
 static void sPrint(Script* sc) {
     Object o;
     if(!sPop(sc, &o) && printer(&o)) {
@@ -229,6 +260,18 @@ static void sGoSub(Script* sc) {
     }
 }
 
+static void sIfGoTo(Script* sc) {
+    int gotoIndex;
+    Object o;
+    if(sReadInt(sc, &gotoIndex) && !sPop(sc, &o)) {
+        if(o.type != OT_BOOL) {
+            sError(sc, "object is not a bool on line %d", sc->line);
+        } else if(!o.data.intValue) {
+            sc->readIndex = gotoIndex;
+        }
+    }
+}
+
 static void sSetReturn(Script* sc) {
     sPop(sc, &sc->returnValue);
 }
@@ -262,11 +305,14 @@ static void sConsumeInstruction(Script* sc) {
         case OP_SET: sSet(sc); break;
         case OP_GET: sGet(sc); break;
         case OP_ADD: sIntBinary(sc, sIntAdd, sFloatAdd); break;
+        case OP_SUB: sIntBinary(sc, sIntSub, sFloatSub); break;
         case OP_MUL: sIntBinary(sc, sIntMul, sFloatMul); break;
+        case OP_LESS: sBoolBinary(sc, sIntLess, sFloatLess); 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_IF_GOTO: sIfGoTo(sc); break;
         case OP_SET_RETURN: sSetReturn(sc); break;
         case OP_RETURN: sReturn(sc); break;
     }
@@ -351,11 +397,14 @@ void sPrintCode(Script* sc) {
             case OP_SET: sPrintInt(sc, "Set"); break;
             case OP_GET: sPrintInt(sc, "Get"); break;
             case OP_ADD: puts("Add"); break;
+            case OP_SUB: puts("Sub"); break;
             case OP_MUL: puts("Mul"); break;
+            case OP_LESS: puts("Less"); break;
             case OP_PRINT: puts("Print"); break;
             case OP_LINE: sPrintInt16(sc, "------------ Line"); break;
             case OP_GOTO: sPrintInt(sc, "GoTo"); break;
             case OP_GOSUB: sPrint2Int(sc, "GoSub"); break;
+            case OP_IF_GOTO: sPrintInt(sc, "If GoTo"); break;
             case OP_SET_RETURN: puts("Set Return"); break;
             case OP_RETURN: puts("Return"); break;
         }

+ 1 - 1
Script.h

@@ -6,7 +6,7 @@
 #include "ByteCode.h"
 #include "Object.h"
 
-#define SCRIPT_STACK_SIZE 50
+#define SCRIPT_STACK_SIZE 100
 #define SCRIPT_ERROR_SIZE 256
 
 typedef struct Script {

+ 18 - 12
Tokenizer.c

@@ -17,6 +17,15 @@ static int16 line = 1;
 static FILE* file = NULL;
 static char error[ERROR_LENGTH] = {'\0'};
 
+typedef struct Literal {
+    const char* name;
+    Token token;
+} Literal;
+
+Literal LITERALS[] = {{"print", T_PRINT},       {"null", T_NULL},     {"true", T_TRUE}, {"false", T_FALSE},
+                      {"function", T_FUNCTION}, {"return", T_RETURN}, {"if", T_IF}};
+const int LITERAL_AMOUNT = sizeof(LITERALS) / sizeof(Literal);
+
 static void tError(const char* format, ...) {
     va_list args;
     va_start(args, format);
@@ -70,18 +79,10 @@ static bool tParseLiteral(int c) {
         buffer[index++] = tRead();
     }
     buffer[index] = '\0';
-    if(strcmp(buffer, "print") == 0) {
-        return tAddToken(T_PRINT);
-    } else if(strcmp(buffer, "null") == 0) {
-        return tAddToken(T_NULL);
-    } else if(strcmp(buffer, "true") == 0) {
-        return tAddToken(T_TRUE);
-    } else if(strcmp(buffer, "false") == 0) {
-        return tAddToken(T_FALSE);
-    } else if(strcmp(buffer, "function") == 0) {
-        return tAddToken(T_FUNCTION);
-    } else if(strcmp(buffer, "return") == 0) {
-        return tAddToken(T_RETURN);
+    for(int i = 0; i < LITERAL_AMOUNT; i++) {
+        if(strcmp(buffer, LITERALS[i].name) == 0) {
+            return tAddToken(LITERALS[i].token);
+        }
     }
     return tAddToken(T_LITERAL) && tAdd(buffer, index + 1);
 }
@@ -137,7 +138,9 @@ static bool tParseToken() {
         case ' ': return true;
         case '\n': line++; return true;
         case '+': return tAddToken(T_ADD);
+        case '-': return tAddToken(T_SUB);
         case '*': return tAddToken(T_MUL);
+        case '<': return tAddToken(T_LESS);
         case '=': return tAddToken(T_SET);
         case ',': return tAddToken(T_COMMA);
         case ';': return tAddToken(T_SEMICOLON);
@@ -233,10 +236,13 @@ const char* tGetTokenName(Token token) {
         case T_TRUE: return "true";
         case T_FALSE: return "false";
         case T_ADD: return "+";
+        case T_SUB: return "-";
         case T_MUL: return "*";
+        case T_LESS: return "<";
         case T_SET: return "=";
         case T_LITERAL: return "literal";
         case T_PRINT: return "print";
+        case T_IF: return "if";
         case T_FUNCTION: return "function";
         case T_RETURN: return "return";
         case T_COMMA: return ",";

+ 3 - 0
Tokenizer.h

@@ -11,10 +11,13 @@ typedef enum Token {
     T_TRUE,
     T_FALSE,
     T_ADD,
+    T_SUB,
     T_MUL,
+    T_LESS,
     T_SET,
     T_LITERAL,
     T_PRINT,
+    T_IF,
     T_FUNCTION,
     T_RETURN,
     T_COMMA,

+ 10 - 0
tests/functions/recursion

@@ -0,0 +1,10 @@
+function fac(n) {
+    if(n < 2) {
+        return 1;
+    }
+    return n * fac(n - 1);
+}
+
+print fac(5);
+print fac(7);
+print fac(12);

+ 3 - 0
tests/functions/recursion.out

@@ -0,0 +1,3 @@
+120
+5040
+479001600

+ 9 - 0
tests/if/if

@@ -0,0 +1,9 @@
+print 5;
+if(true) {
+    print 6;
+}
+print 7;
+if(false) {
+    print 8;
+}
+print 9;

+ 4 - 0
tests/if/if.out

@@ -0,0 +1,4 @@
+5
+6
+7
+9