Prechádzať zdrojové kódy

More system functions, comments, arrays, garbage collector

Kajetan Johannes Hammerle 6 dní pred
rodič
commit
6352805fd4
15 zmenil súbory, kde vykonal 425 pridanie a 33 odobranie
  1. 30 0
      examples/term.basic
  2. 105 7
      src/Code.c
  3. 18 2
      src/Code.h
  4. 22 18
      src/Compiler.c
  5. 3 0
      src/Main.c
  6. 8 0
      src/Memory.c
  7. 2 0
      src/Memory.h
  8. 165 4
      src/SystemFunctions.c
  9. 1 0
      src/SystemFunctions.h
  10. 5 1
      src/Tokenizer.c
  11. 2 0
      src/Tokenizer.h
  12. 15 0
      src/Utils.h
  13. 2 1
      test/Add.basic
  14. 33 0
      test/Array.basic
  15. 14 0
      test/Array.basic_result

+ 30 - 0
examples/term.basic

@@ -0,0 +1,30 @@
+setPrintNewline(0)
+hideCursor()
+clear()
+
+x = getWidth() / 2
+y = getHeight() / 2
+
+for i = 0 to 500
+    x = x + random(0-1, 1)
+    y = y + random(0-1, 1)
+    if x < 0
+        x = 0
+    end
+    if y < 0
+        y = 0
+    end
+    if x > getWidth() - 1
+        x = getWidth() - 1
+    end
+    if y >= getHeight() - 1
+        y = getHeight() - 1
+    end
+    resetCursor()
+    print(x, " ", y)
+    moveCursorRight(x)
+    moveCursorDown(y)
+    print("X")
+    sleep(0, 1000000)
+end
+sleep(2, 0)

+ 105 - 7
src/Code.c

@@ -7,6 +7,7 @@
 #include "Constants.h"
 #include "Memory.h"
 #include "SystemFunctions.h"
+#include "Utils.h"
 
 void codeInit(Code* c) {
     *c = (Code){};
@@ -16,6 +17,7 @@ void codeInit(Code* c) {
 void codeDestroy(Code* c) {
     bufferDestroy(&c->code);
     memoryFree(c->stack);
+    CLEAN_LIST(c, Allocation, allocations);
     *c = (Code){};
 }
 
@@ -31,11 +33,6 @@ 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), "%zu | " format, \
-        c->code.readIndex __VA_OPT__(, ) __VA_ARGS__)
-
 [[nodiscard]] static bool codeReadI32(Code* c, i32* i) {
     return bufferReadI32(&c->code, i);
 }
@@ -261,13 +258,40 @@ static bool iReadVariable(Code* c) {
     }
     address = iConvertAddress(c, address);
     if((size_t)address >= c->stackIndex) {
-        SET_ERROR(
-            "ReadVariable with invalid address %d %zu", address, c->stackIndex);
+        SET_ERROR("ReadVariable with invalid address");
         return true;
     }
     return iPushValue(c, c->stack[address]);
 }
 
+static bool iReadVariableArray(Code* c) {
+    i32 address = 0;
+    if(codeReadI32(c, &address)) {
+        SET_ERROR("ReadVariableArray without address");
+        return true;
+    }
+    address = iConvertAddress(c, address);
+    if((size_t)address >= c->stackIndex) {
+        SET_ERROR("ReadVariableArray with invalid address");
+        return true;
+    }
+    Value* v = c->stack + address;
+    if(v->type != VT_ARRAY) {
+        SET_ERROR("ReadVariableArray on non array");
+        return true;
+    } else if(v->data == -1) {
+        SET_ERROR("ReadVariableArray on null array");
+        return true;
+    }
+    Allocation* array = memoryConvertToPointer(v->data);
+    POP_VALUE(index);
+    if(index.data < 0 || index.data >= array->values[0].data) {
+        SET_ERROR("ReadVariableArray with invalid index");
+        return true;
+    }
+    return iPushValue(c, array->values[index.data + 1]);
+}
+
 static bool iSetVariable(Code* c) {
     i32 address = 0;
     if(codeReadI32(c, &address)) {
@@ -284,6 +308,36 @@ static bool iSetVariable(Code* c) {
     return false;
 }
 
+static bool iSetVariableArray(Code* c) {
+    i32 address = 0;
+    if(codeReadI32(c, &address)) {
+        SET_ERROR("SetVariableArray without address");
+        return true;
+    }
+    address = iConvertAddress(c, address);
+    if((size_t)address >= c->stackIndex) {
+        SET_ERROR("SetVariableArray with invalid address");
+        return true;
+    }
+    Value* v = c->stack + address;
+    if(v->type != VT_ARRAY) {
+        SET_ERROR("SetVariableArray on non array");
+        return true;
+    } else if(v->data == -1) {
+        SET_ERROR("SetVariableArray on null array");
+        return true;
+    }
+    Allocation* array = memoryConvertToPointer(v->data);
+    POP_VALUE(value);
+    POP_VALUE(index);
+    if(index.data < 0 || index.data >= array->values[0].data) {
+        SET_ERROR("SetVariableArray with invalid index");
+        return true;
+    }
+    array->values[index.data + 1] = value;
+    return false;
+}
+
 static bool iPushStackVariables(Code* c) {
     i32 amount = 0;
     if(codeReadI32(c, &amount)) {
@@ -344,7 +398,9 @@ static bool execute(Code* c, Instruction command) {
         case JUMP_SUB: return iJumpSub(c);
         case RETURN_VALUE: return iReturnValue(c);
         case READ_VARIABLE: return iReadVariable(c);
+        case READ_VARIABLE_ARRAY: return iReadVariableArray(c);
         case SET_VARIABLE: return iSetVariable(c);
+        case SET_VARIABLE_ARRAY: return iSetVariableArray(c);
         case PUSH_STACK_VARIABLES: return iPushStackVariables(c);
         case AND: return iAnd(c);
         case OR: return iOr(c);
@@ -417,7 +473,9 @@ void codeDump(const Code* code) {
             DUMP_INT2(JUMP_SUB);
             DUMP_INT(RETURN_VALUE);
             DUMP_INT(READ_VARIABLE);
+            DUMP_INT(READ_VARIABLE_ARRAY);
             DUMP_INT(SET_VARIABLE);
+            DUMP_INT(SET_VARIABLE_ARRAY);
             DUMP_INT(PUSH_STACK_VARIABLES);
             DUMP(AND);
             DUMP(OR);
@@ -438,3 +496,43 @@ void codeDump(const Code* code) {
         }
     }
 }
+
+static void visit(Value* v) {
+    v->visitMarker = true;
+    if(v->type == VT_ARRAY) {
+        Allocation* a = memoryConvertToPointer(v->data);
+        if(a[0].values->visitMarker) {
+            return;
+        }
+        Value* end = a->values + a->values[0].data + 1;
+        for(Value* i = a->values; i != end; i++) {
+            visit(i);
+        }
+    }
+}
+
+void codeRunCollector(Code* c) {
+    for(Allocation* a = c->allocations; a != nullptr; a = a->next) {
+        a->values[0].visitMarker = false;
+    }
+    Value* end = c->stack + c->stackIndex;
+    for(Value* v = c->stack; v != end; v++) {
+        visit(v);
+    }
+    Allocation* prev = nullptr;
+    Allocation* a = c->allocations;
+    while(a != nullptr) {
+        Allocation* next = a->next;
+        if(!a->values[0].visitMarker) {
+            if(prev != nullptr) {
+                prev->next = a->next;
+            } else {
+                c->allocations = a->next;
+            }
+            memoryFree(a);
+        } else {
+            prev = a;
+        }
+        a = next;
+    }
+}

+ 18 - 2
src/Code.h

@@ -5,16 +5,23 @@
 #include "Error.h"
 #include "Types.h"
 
-typedef enum : u8 { VT_INT32, VT_CONSTANT_STRING } ValueType;
+typedef enum : u8 { VT_INT32, VT_ARRAY, VT_CONSTANT_STRING } ValueType;
 
 typedef struct {
     ValueType type;
-    u8 reserved1;
+    u8 visitMarker;
     u8 reserved2;
     u8 reserved3;
     i32 data;
 } Value;
 
+typedef struct Allocation Allocation;
+
+struct Allocation {
+    Allocation* next;
+    Value values[];
+};
+
 #define INT_VALUE(value) ((Value){.type = VT_INT32, .data = (value)})
 
 static_assert(sizeof(Value) == 8);
@@ -32,7 +39,9 @@ typedef enum : u8 {
     JUMP_SUB,
     RETURN_VALUE,
     READ_VARIABLE,
+    READ_VARIABLE_ARRAY,
     SET_VARIABLE,
+    SET_VARIABLE_ARRAY,
     PUSH_STACK_VARIABLES,
     AND,
     OR,
@@ -51,6 +60,7 @@ typedef struct {
     Buffer code;
     Error error;
     Value* stack;
+    Allocation* allocations;
     size_t maxStackSize;
     size_t stackIndex;
     i32 localVariableIndex;
@@ -64,6 +74,12 @@ void codeRun(Code* code);
 bool codeHasRunError(const Code* code);
 const char* codeGetRunError(const Code* code);
 
+void codeRunCollector(Code* code);
 void codeDump(const Code* code);
 
+#define SET_ERROR(format, ...)                                 \
+    snprintf(                                                  \
+        c->error.text, sizeof(c->error.text), "%zu | " format, \
+        c->code.readIndex __VA_OPT__(, ) __VA_ARGS__)
+
 #endif

+ 22 - 18
src/Compiler.c

@@ -9,6 +9,7 @@
 #include "Constants.h"
 #include "Memory.h"
 #include "SystemFunctions.h"
+#include "Utils.h"
 
 typedef struct Variable Variable;
 
@@ -212,26 +213,15 @@ static void addLoopJump(Context* c, size_t address, bool stop) {
     c->loopJumps = 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);
+    CLEAN_LIST(c, Return, returns);
 }
 
 static void cleanContext(Context* c) {
-    CLEAN_LIST(Variable, variables);
-    CLEAN_LIST(Function, functions);
+    CLEAN_LIST(c, Variable, variables);
+    CLEAN_LIST(c, Function, functions);
     cleanReturns(c);
-    CLEAN_LIST(BreakContinue, loopJumps);
+    CLEAN_LIST(c, BreakContinue, loopJumps);
 }
 
 [[noreturn]] static void unexpectedToken(Context* c, Token token, int line) {
@@ -268,9 +258,17 @@ static void consumeNewline(Context* c) {
     c->line++;
 }
 
+static void compileExpression(Context* c);
+
 static void compileReadVariable(Context* c, Token token, bool forceGlobal) {
+    Instruction i = READ_VARIABLE;
+    if(consumeTokenIf(c, TT_OPEN_SQUARE_BRACKET)) {
+        compileExpression(c);
+        consumeToken(c, TT_CLOSE_SQUARE_BRACKET);
+        i = READ_VARIABLE_ARRAY;
+    }
     i32 index = addVariable(c, token.stringValue, forceGlobal);
-    codePushInstructionI32(c, READ_VARIABLE, index);
+    codePushInstructionI32(c, i, index);
 }
 
 static void compileCallFunction(Context* c, Token t);
@@ -396,9 +394,15 @@ static void compileIf(Context* c) {
 }
 
 static void compileSetVariable(Context* c, const char* name, bool forceGlobal) {
+    Instruction i = SET_VARIABLE;
+    if(consumeTokenIf(c, TT_OPEN_SQUARE_BRACKET)) {
+        compileExpression(c);
+        consumeToken(c, TT_CLOSE_SQUARE_BRACKET);
+        i = SET_VARIABLE_ARRAY;
+    }
     consumeToken(c, TT_ASSIGN);
     compileExpression(c);
-    codePushInstructionI32(c, SET_VARIABLE, addVariable(c, name, forceGlobal));
+    codePushInstructionI32(c, i, addVariable(c, name, forceGlobal));
 }
 
 static void rewriteLoopJumps(
@@ -575,7 +579,7 @@ static void compileReturn(Context* c) {
 
 static void compileLineLiteral(Context* c, Token token) {
     const char* s = token.stringValue;
-    if(peekToken(c, TT_ASSIGN)) {
+    if(peekToken(c, TT_OPEN_SQUARE_BRACKET) || peekToken(c, TT_ASSIGN)) {
         compileSetVariable(c, s, false);
     } else if(peekToken(c, TT_OPEN_ROUND_BRACKET)) {
         compileCallFunction(c, token);

+ 3 - 0
src/Main.c

@@ -4,6 +4,7 @@
 #include "Code.h"
 #include "Compiler.h"
 #include "Memory.h"
+#include "SystemFunctions.h"
 #include "Tokenizer.h"
 
 int main(int argCount, const char** args) {
@@ -15,6 +16,8 @@ int main(int argCount, const char** args) {
     memoryInit(heap, sizeof(heap));
     // memoryDump();
 
+    initSystemFunctions();
+
     Tokenizer t;
     Error e = tokenizerInit(&t, args[1]);
     if(hasError(&e)) {

+ 8 - 0
src/Memory.c

@@ -125,3 +125,11 @@ void memoryDump() {
     }
     fprintf(stderr, " = %u\n", sum);
 }
+
+i32 memoryConvertToIndex(void* p) {
+    return memoryGetSlotIndex((Slot*)p);
+}
+
+void* memoryConvertToPointer(i32 index) {
+    return memory + index;
+}

+ 2 - 0
src/Memory.h

@@ -7,5 +7,7 @@ void memoryInit(void* p, u32 n);
 void* memoryAllocate(size_t bytes);
 void memoryFree(void* p);
 void memoryDump();
+i32 memoryConvertToIndex(void* p);
+void* memoryConvertToPointer(i32 index);
 
 #endif

+ 165 - 4
src/SystemFunctions.c

@@ -2,34 +2,189 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <sys/ioctl.h>
+#include <threads.h>
+
+#include "Memory.h"
+
+#define RETURN_INT(value)  \
+    *r = INT_VALUE(value); \
+    return false
+
+static u32 seed = 0;
+static bool printNewline = true;
 
 static bool sfPrint(Code* c, Value* r, Value* vs, i32 n) {
     for(i32 i = 0; i < n; i++) {
         Value* v = vs + i;
         switch(v->type) {
             case VT_INT32: printf("%d", v->data); break;
+            case VT_ARRAY: printf("array"); break;
             case VT_CONSTANT_STRING:
                 printf("%s", c->code.data + v->data);
                 break;
         }
     }
-    putchar('\n');
-    *r = INT_VALUE(0);
+    if(printNewline) {
+        putchar('\n');
+    }
+    RETURN_INT(0);
+}
+
+static bool sfSetPrintNewline(Code*, Value* r, Value* vs, i32) {
+    printNewline = vs[0].data;
+    RETURN_INT(0);
+}
+
+#define ESC "\33["
+#define esc(s) fputs(ESC s, stdout)
+
+static bool sfClear(Code*, Value* r, Value*, i32) {
+    esc("2J");
+    RETURN_INT(0);
+}
+
+static bool sfGetWidth(Code*, Value* r, Value*, i32) {
+    struct winsize w;
+    if(ioctl(0, TIOCGWINSZ, &w)) {
+        RETURN_INT(0);
+    }
+    RETURN_INT(w.ws_col);
+}
+
+static bool sfGetHeight(Code*, Value* r, Value*, i32) {
+    struct winsize w;
+    if(ioctl(0, TIOCGWINSZ, &w)) {
+        RETURN_INT(0);
+    }
+    RETURN_INT(w.ws_row);
+}
+
+static bool sfClearLine(Code*, Value* r, Value*, i32) {
+    esc("2K\r");
+    RETURN_INT(0);
+}
+
+static bool sfHideCursor(Code*, Value* r, Value*, i32) {
+    esc("?25l");
+    RETURN_INT(0);
+}
+
+static bool sfShowCursor(Code*, Value* r, Value*, i32) {
+    esc("?25h");
+    RETURN_INT(0);
+}
+
+static bool sfResetCursor(Code*, Value* r, Value*, i32) {
+    esc("H");
+    RETURN_INT(0);
+}
+
+static bool sfMoveCursorLeft(Code*, Value* r, Value* vs, i32) {
+    if(vs[0].data > 0) {
+        printf(ESC "%dD", vs[0].data);
+    }
+    RETURN_INT(0);
+}
+
+static bool sfMoveCursorRight(Code*, Value* r, Value* vs, i32) {
+    if(vs[0].data > 0) {
+        printf(ESC "%dC", vs[0].data);
+    }
+    RETURN_INT(0);
+}
+
+static bool sfMoveCursorUp(Code*, Value* r, Value* vs, i32) {
+    if(vs[0].data > 0) {
+        printf(ESC "%dA", vs[0].data);
+    }
+    RETURN_INT(0);
+}
+
+static bool sfMoveCursorDown(Code*, Value* r, Value* vs, i32) {
+    if(vs[0].data > 0) {
+        printf(ESC "%dB", vs[0].data);
+    }
+    RETURN_INT(0);
+}
+
+static bool sfSleep(Code*, Value* r, Value* vs, i32) {
+    struct timespec t = {.tv_sec = vs[0].data, .tv_nsec = vs[1].data};
+    thrd_sleep(&t, nullptr);
+    RETURN_INT(0);
+}
+
+static bool sfRandom(Code*, Value* r, Value* vs, i32) {
+    seed = seed * 1664525u + 1013904223u;
+    u32 s = seed % (u32)(vs[1].data + 1 - vs[0].data);
+    RETURN_INT(vs[0].data + (i32)s);
+}
+
+static bool codeAllocate(Code* c, Value* v, i32 n) {
+    v->type = VT_ARRAY;
+    v->data = -1;
+    if(n <= 0) {
+        return false;
+    }
+    codeRunCollector(c);
+    size_t s = sizeof(Allocation) + ((size_t)n + 1) * sizeof(Value);
+    Allocation* a = memoryAllocate(s);
+    if(a == nullptr) {
+        SET_ERROR("Out of memory");
+        return true;
+    }
+    memset(a, 0, s);
+    a->next = c->allocations;
+    c->allocations = a;
+    a->values[0].type = VT_INT32;
+    a->values[0].data = n;
+    v->data = memoryConvertToIndex(a);
     return false;
 }
 
+static bool sfArray(Code* c, Value* r, Value* vs, i32) {
+    return codeAllocate(c, r, vs[0].data);
+}
+
+static bool sfGetAllocations(Code* c, Value* r, Value*, i32) {
+    codeRunCollector(c);
+    i32 counter = 0;
+    for(Allocation* a = c->allocations; a != nullptr; a = a->next) {
+        counter++;
+    }
+    RETURN_INT(counter);
+}
+
 typedef struct {
     SystemFunction function;
     i32 arguments;
     const char* name;
 } FunctionEntry;
 
-static FunctionEntry systemFunctions[] = {{sfPrint, -1, "print"}};
+static FunctionEntry systemFunctions[] = {
+    {sfPrint, -1, "print"},
+    {sfSetPrintNewline, 1, "setPrintNewline"},
+    {sfClear, 0, "clear"},
+    {sfSleep, 2, "sleep"},
+    {sfGetWidth, 0, "getWidth"},
+    {sfGetHeight, 0, "getHeight"},
+    {sfClearLine, 0, "clearLine"},
+    {sfHideCursor, 0, "hideCursor"},
+    {sfShowCursor, 0, "showCursor"},
+    {sfResetCursor, 0, "resetCursor"},
+    {sfMoveCursorLeft, 1, "moveCursorLeft"},
+    {sfMoveCursorRight, 1, "moveCursorRight"},
+    {sfMoveCursorUp, 1, "moveCursorUp"},
+    {sfMoveCursorDown, 1, "moveCursorDown"},
+    {sfRandom, 2, "random"},
+    {sfArray, 1, "array"},
+    {sfGetAllocations, 0, "getAllocations"},
+};
 static constexpr i32 amount = sizeof(systemFunctions) / sizeof(FunctionEntry);
 
 i32 getSystemFunctionIndex(const char* s) {
     for(i32 i = 0; i < amount; i++) {
-        if(strcmp(systemFunctions->name, s) == 0) {
+        if(strcmp(systemFunctions[i].name, s) == 0) {
             return i;
         }
     }
@@ -49,3 +204,9 @@ SystemFunction getSystemFunction(i32 index) {
     }
     return nullptr;
 }
+
+void initSystemFunctions() {
+    struct timespec t = {};
+    timespec_get(&t, TIME_UTC);
+    seed = (u32)t.tv_nsec;
+}

+ 1 - 0
src/SystemFunctions.h

@@ -8,5 +8,6 @@ typedef bool (*SystemFunction)(Code* c, Value* r, Value* vs, i32 n);
 i32 getSystemFunctionIndex(const char* s);
 i32 getSystemFunctionArguments(i32 index);
 SystemFunction getSystemFunction(i32 index);
+void initSystemFunctions();
 
 #endif

+ 5 - 1
src/Tokenizer.c

@@ -167,7 +167,7 @@ static void tParseLineString(TState* t, const char* s) {
             s = tokenizerAddNumber(t, s);
         } else if(c == '"') {
             s = tokenizerAddString(t, s);
-        } else if(c == '\n') {
+        } else if(c == '\n' || c == '#') {
             tAddToken(t, TT_NEWLINE);
             break;
         } else if(c == ' ') {
@@ -185,6 +185,8 @@ static void tParseLineString(TState* t, const char* s) {
         SIMPLE_TOKEN2('<', '=', TT_SMALLER, TT_SMALLER_OR_EQUAL)
         SIMPLE_TOKEN('(', TT_OPEN_ROUND_BRACKET)
         SIMPLE_TOKEN(')', TT_CLOSE_ROUND_BRACKET)
+        SIMPLE_TOKEN('[', TT_OPEN_SQUARE_BRACKET)
+        SIMPLE_TOKEN(']', TT_CLOSE_SQUARE_BRACKET)
         else if(c == '&' && s[1] == '&') {
             s += 2;
             tAddToken(t, TT_AND);
@@ -312,6 +314,8 @@ void tokenizerPrintToken(const Token* token, char* buffer, size_t n) {
         TOKEN_PRINTER(TT_CONTINUE, "Continue");
         TOKEN_PRINTER(TT_OPEN_ROUND_BRACKET, "(");
         TOKEN_PRINTER(TT_CLOSE_ROUND_BRACKET, ")");
+        TOKEN_PRINTER(TT_OPEN_SQUARE_BRACKET, "[");
+        TOKEN_PRINTER(TT_CLOSE_SQUARE_BRACKET, "]");
         TOKEN_PRINTER(TT_NEWLINE, "Newline");
         TOKEN_PRINTER(TT_INVALID, "Invalid");
     }

+ 2 - 0
src/Tokenizer.h

@@ -35,6 +35,8 @@ typedef enum : u8 {
     TT_CONTINUE,
     TT_OPEN_ROUND_BRACKET,
     TT_CLOSE_ROUND_BRACKET,
+    TT_OPEN_SQUARE_BRACKET,
+    TT_CLOSE_SQUARE_BRACKET,
     TT_NEWLINE,
     TT_INVALID
 } TokenType;

+ 15 - 0
src/Utils.h

@@ -0,0 +1,15 @@
+#ifndef BASIC_UTILS_H
+#define BASIC_UTILS_H
+
+#define CLEAN_LIST(var, Type, field) \
+    do {                             \
+        Type* v = var->field;        \
+        while(v != nullptr) {        \
+            Type* next = v->next;    \
+            memoryFree(v);           \
+            v = next;                \
+        }                            \
+        var->field = nullptr;        \
+    } while(false)
+
+#endif

+ 2 - 1
test/Add.basic

@@ -1,7 +1,8 @@
 print(1 + 20)
 print(1 + 20 + 4)
 print(1 + 20 + 4 + 6)
-print(5 - 6)
+# dummy comment
+print(5 - 6) # another dummy
 print(5 * 6)
 print(16 / 5)
 print(1 + 2 * 3)

+ 33 - 0
test/Array.basic

@@ -0,0 +1,33 @@
+print(getAllocations())
+a = array(5)
+print(getAllocations())
+a[0] = 5
+a[1] = 20
+a[2] = 40
+a[3] = 60
+a[4] = 80
+
+for i = 0 to 4
+    print(a[i])
+end
+
+b = array(7)
+print(getAllocations())
+c = array(7)
+print(getAllocations())
+for i = 0 to 40
+    a = array(5)
+end
+print(getAllocations())
+
+d = array(5)
+print(getAllocations())
+d[0] = array(3)
+d[1] = array(4)
+d[2] = d
+print(d)
+print(getAllocations())
+
+d = 5
+print(getAllocations())
+

+ 14 - 0
test/Array.basic_result

@@ -0,0 +1,14 @@
+0
+1
+5
+20
+40
+60
+80
+2
+3
+3
+4
+array
+6
+3