From fc2a0328d5dc36ecb9ad6cee36242c5e4d70ab1e Mon Sep 17 00:00:00 2001
From: nakst <>
Date: Tue, 1 Feb 2022 10:25:21 +0000
Subject: [PATCH] script engine coroutines

---
 util/script.c | 1028 +++++++++++++++++++++++++++++++------------------
 1 file changed, 663 insertions(+), 365 deletions(-)

diff --git a/util/script.c b/util/script.c
index ced3417..91fb8b5 100644
--- a/util/script.c
+++ b/util/script.c
@@ -1,5 +1,5 @@
 // TODO Basic missing features:
-// 	- Other list operations: add, insert, insert_many, delete, delete_many, delete_last, delete_all.
+// 	- Other list operations: insert, insert_many, delete, delete_many, delete_last, delete_all.
 // 	- Maps: T[int], T[str].
 // 	- Control flow: break, continue.
 // 	- Other operators: remainder, bitwise shifts, unary minus, bitwise AND/OR/XOR/NOT, ternary.
@@ -7,7 +7,6 @@
 // 	- Resolving type identifiers when structs or function pointers contain references to other structs or function pointers.
 
 // TODO Larger missing features:
-// 	- Coroutines.
 // 	- Serialization.
 // 	- Debugging.
 // 	- Verbose mode, where every external call is logged, every variable modification is logged, every line is logged, etc? Saving output to file.
@@ -128,6 +127,7 @@
 #define T_OP_ASSERT           (152)
 #define T_OP_CURRY            (153)
 #define T_OP_ASYNC            (154)
+#define T_OP_FIND_AND_DELETE  (155)
 
 #define T_IF                  (160)
 #define T_WHILE               (161)
@@ -151,6 +151,7 @@
 #define T_OPTION              (179)
 #define T_IMPORT              (180)
 #define T_INLINE              (181)
+#define T_AWAIT               (182)
 
 typedef struct Token {
 	struct ImportData *module;
@@ -219,10 +220,10 @@ typedef struct FunctionBuilder {
 } FunctionBuilder;
 
 typedef struct BackTraceItem {
-	uint32_t instructionPointer;
-	uint32_t variableBase : 30,
+	uint32_t instructionPointer : 30,
 		 popResult : 1,
 		 assertResult : 1;
+	int32_t variableBase : 30;
 } BackTraceItem;
 
 typedef struct HeapEntry {
@@ -258,15 +259,37 @@ typedef struct HeapEntry {
 	};
 } HeapEntry;
 
-typedef struct ExecutionContext {
-	Value *globalVariables;
-	bool *globalVariableIsManaged;
-	size_t globalVariableCount;
-
+typedef struct CoroutineState {
+	Value *localVariables;
+	bool *localVariableIsManaged;
+	size_t localVariableCount;
+	size_t localVariablesAllocated;
 	Value stack[50]; // TODO Merge with variables?
 	bool stackIsManaged[50];
 	uintptr_t stackPointer;
 	size_t stackEntriesAllocated;
+	BackTraceItem backTrace[300];
+	uintptr_t backTracePointer;
+	uint64_t id;
+	int64_t unblockedBy;
+	struct CoroutineState *nextCoroutine;
+	struct CoroutineState **previousCoroutineLink;
+	struct CoroutineState *nextUnblockedCoroutine;
+	struct CoroutineState **previousUnblockedCoroutineLink;
+	struct CoroutineState **waiters;
+	size_t waiterCount;
+	size_t waitersAllocated;
+	struct CoroutineState ***waitingOn;
+	size_t waitingOnCount;
+	bool awaiting, startedByAsync;
+	uintptr_t instructionPointer;
+	uintptr_t variableBase;
+} CoroutineState;
+
+typedef struct ExecutionContext {
+	Value *globalVariables;
+	bool *globalVariableIsManaged;
+	size_t globalVariableCount;
 
 	HeapEntry *heap;
 	uintptr_t heapFirstUnusedEntry;
@@ -277,12 +300,10 @@ typedef struct ExecutionContext {
 	char *scriptPersistFile;
 	struct ImportData *mainModule;
 
-	Value *localVariables;
-	bool *localVariableIsManaged;
-	size_t localVariableCount;
-	size_t localVariablesAllocated;
-	BackTraceItem backTrace[300];
-	uintptr_t backTracePointer;
+	CoroutineState *c; // Active coroutine.
+	CoroutineState *allCoroutines;
+	CoroutineState *unblockedCoroutines;
+	uint64_t lastCoroutineID;
 } ExecutionContext;
 
 typedef struct ExternalFunction {
@@ -306,6 +327,7 @@ Node globalExpressionTypeInt = { .type = T_INT };
 Node globalExpressionTypeFloat = { .type = T_FLOAT };
 Node globalExpressionTypeBool = { .type = T_BOOL };
 Node globalExpressionTypeStr = { .type = T_STR };
+Node globalExpressionTypeIntList = { .type = T_LIST, .firstChild = &globalExpressionTypeInt };
 
 // Global variables:
 char *scriptSourceDirectory;
@@ -320,6 +342,7 @@ Node *ParseBlock(Tokenizer *tokenizer);
 Node *ParseExpression(Tokenizer *tokenizer, bool allowAssignment, uint8_t precedence);
 void ScriptPrintNode(Node *node, int indent);
 bool ScriptLoad(Tokenizer tokenizer, ExecutionContext *context, ImportData *importData);
+void ScriptFreeCoroutine(CoroutineState *c);
 uintptr_t HeapAllocate(ExecutionContext *context);
 
 // --------------------------------- Platform layer definitions.
@@ -338,6 +361,7 @@ void PrintError(Tokenizer *tokenizer, const char *format, ...);
 void PrintError2(Tokenizer *tokenizer, Node *node, const char *format, ...);
 void PrintError3(const char *format, ...);
 void PrintError4(ExecutionContext *context, uint32_t instructionPointer, const char *format, ...);
+void PrintBackTrace(ExecutionContext *context, uint32_t instructionPointer, CoroutineState *c, const char *prefix);
 void *FileLoad(const char *path, size_t *length);
 
 // --------------------------------- Base module.
@@ -478,9 +502,10 @@ uint8_t TokenLookupPrecedence(uint8_t t) {
 	if (t == T_MINUS)           return 50;
 	if (t == T_ASTERISK)        return 60;
 	if (t == T_SLASH)           return 60;
+	if (t == T_LOGICAL_NOT)     return 70;
 	if (t == T_DOT)             return 80;
 	if (t == T_COLON)           return 80;
-	if (t == T_LOGICAL_NOT)     return 90;
+	if (t == T_AWAIT)           return 90;
 	if (t == T_LEFT_ROUND)      return 100;
 	Assert(false);
 }
@@ -570,6 +595,7 @@ Token TokenNext(Tokenizer *tokenizer) {
 			else if KEYWORD("#option") token.type = T_OPTION;
 			else if KEYWORD("#persist") token.type = T_PERSIST;
 			else if KEYWORD("assert") token.type = T_ASSERT;
+			else if KEYWORD("await") token.type = T_AWAIT;
 			else if KEYWORD("bool") token.type = T_BOOL;
 			else if KEYWORD("else") token.type = T_ELSE;
 			else if KEYWORD("false") token.type = T_FALSE;
@@ -893,6 +919,9 @@ Node *ParseExpression(Tokenizer *tokenizer, bool allowAssignment, uint8_t preced
 			link = &item->sibling;
 			needComma = true;
 		}
+	} else if (node->token.type == T_AWAIT) {
+		node->type = T_AWAIT;
+		node->firstChild = ParseExpression(tokenizer, false, TokenLookupPrecedence(node->token.type));
 	} else {
 		PrintError2(tokenizer, node, "Expected an expression. "
 				"Expressions can start with a variable identifier, a string literal, a number, 'len', 'new', '!', '[' or '('.\n");
@@ -1004,8 +1033,11 @@ Node *ParseIf(Tokenizer *tokenizer) {
 		node->firstChild->sibling = ParseBlock(tokenizer);
 		if (!node->firstChild->sibling) return NULL;
 	} else {
-		node->firstChild->sibling = ParseExpression(tokenizer, true, 0);
-		if (!node->firstChild->sibling) return NULL;
+		Node *wrapper = (Node *) AllocateFixed(sizeof(Node));
+		wrapper->type = T_BLOCK;
+		wrapper->firstChild = ParseExpression(tokenizer, true, 0);
+		if (!wrapper->firstChild) return NULL;
+		node->firstChild->sibling = wrapper;
 
 		Token semicolon = TokenNext(tokenizer);
 
@@ -2114,7 +2146,7 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
 
 		Token token = node->token;
 		Node *arguments[2] = { 0 };
-		bool returnsItem = false, returnsInt = false, simple = true;
+		bool returnsItem = false, returnsInt = false, returnsBool = false, simple = true;
 		uint8_t op;
 
 		if (isList && KEYWORD("resize")) arguments[0] = &globalExpressionTypeInt, op = T_OP_RESIZE;
@@ -2122,6 +2154,7 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
 		else if (isList && KEYWORD("insert")) arguments[0] = expressionType->firstChild, arguments[1] = &globalExpressionTypeInt, op = T_OP_INSERT;
 		else if (isList && KEYWORD("insert_many")) arguments[0] = &globalExpressionTypeInt, arguments[1] = &globalExpressionTypeInt, op = T_OP_INSERT_MANY;
 		else if (isList && KEYWORD("delete")) arguments[0] = &globalExpressionTypeInt, op = T_OP_DELETE;
+		else if (isList && KEYWORD("find_and_delete")) arguments[0] = expressionType->firstChild, op = T_OP_FIND_AND_DELETE, returnsBool = true;
 		else if (isList && KEYWORD("delete_many")) arguments[0] = &globalExpressionTypeInt, arguments[1] = &globalExpressionTypeInt, op = T_OP_DELETE_MANY;
 		else if (isList && KEYWORD("delete_last")) op = T_OP_DELETE_LAST;
 		else if (isList && KEYWORD("delete_all")) op = T_OP_DELETE_ALL;
@@ -2196,6 +2229,11 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
 			return false;
 		}
 
+		if (op == T_OP_FIND_AND_DELETE && ASTMatching(arguments[0], &globalExpressionTypeFloat)) {
+			PrintError2(tokenizer, node, "The find_and_delete operation cannot be used with floats.\n");
+			return false;
+		}
+
 		if (simple) {
 			Node *argument1 = node->firstChild->sibling->firstChild;
 			Node *argument2 = argument1 ? argument1->sibling : NULL;
@@ -2217,7 +2255,9 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
 				return false;
 			}
 
-			node->expressionType = returnsItem ? expressionType->firstChild : returnsInt ? &globalExpressionTypeInt : NULL;
+			node->expressionType = returnsItem ? expressionType->firstChild 
+				: returnsInt ? &globalExpressionTypeInt 
+				: returnsBool ? &globalExpressionTypeBool : NULL;
 		}
 
 		node->operationType = op;
@@ -2238,7 +2278,10 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
 		Node *item = node->firstChild;
 		node->expressionType = (Node *) AllocateFixed(sizeof(Node));
 		node->expressionType->type = T_LIST;
-		node->expressionType->firstChild = item->expressionType;
+		Node *copy = (Node *) AllocateFixed(sizeof(Node));
+		*copy = *item->expressionType;
+		copy->sibling = NULL;
+		node->expressionType->firstChild = copy;
 
 		while (item) {
 			if (!ASTMatching(node->expressionType->firstChild, item->expressionType)) {
@@ -2248,6 +2291,13 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
 
 			item = item->sibling;
 		}
+	} else if (node->type == T_AWAIT) {
+		if (!ASTMatching(node->firstChild->expressionType, &globalExpressionTypeIntList)) {
+			PrintError2(tokenizer, node, "Expected a list of task IDs to wait on.\n");
+			return false;
+		}
+
+		node->expressionType = &globalExpressionTypeInt;
 	} else {
 		PrintDebug("ASTSetTypes %d\n", node->type);
 		Assert(false);
@@ -2658,7 +2708,8 @@ bool FunctionBuilderRecurse(Tokenizer *tokenizer, Node *node, FunctionBuilder *b
 		}
 
 		if (node->type == T_BLOCK && child->expressionType && child->expressionType->type != T_VOID) {
-			if (child->type == T_CALL) {
+			if (child->type == T_CALL || child->type == T_AWAIT 
+					|| (child->type == T_COLON && child->operationType == T_OP_FIND_AND_DELETE)) {
 				uint8_t b = T_POP;
 				FunctionBuilderAppend(builder, &b, sizeof(b));
 			} else if (child->type == T_DECLARE || child->type == T_EQUALS) {
@@ -2683,7 +2734,7 @@ bool FunctionBuilderRecurse(Tokenizer *tokenizer, Node *node, FunctionBuilder *b
 		uint8_t b = T_END_FUNCTION;
 		FunctionBuilderAddLineNumber(builder, node);
 		FunctionBuilderAppend(builder, &b, sizeof(b));
-	} else if (node->type == T_ASSERT || node->type == T_NULL || node->type == T_LOGICAL_NOT) {
+	} else if (node->type == T_ASSERT || node->type == T_NULL || node->type == T_LOGICAL_NOT || node->type == T_AWAIT) {
 		FunctionBuilderAddLineNumber(builder, node);
 		FunctionBuilderAppend(builder, &node->type, sizeof(node->type));
 	} else if (node->type == T_ADD || node->type == T_MINUS || node->type == T_ASTERISK || node->type == T_SLASH) {
@@ -2960,18 +3011,24 @@ uintptr_t HeapAllocate(ExecutionContext *context) {
 			}
 		}
 
-		for (uintptr_t i = 0; i < context->localVariableCount; i++) {
-			if (context->localVariableIsManaged[i]) {
-				HeapGarbageCollectMark(context, context->localVariables[i].i);
+		CoroutineState *c = context->allCoroutines;
+
+		while (c) {
+			for (uintptr_t i = 0; i < c->localVariableCount; i++) {
+				if (c->localVariableIsManaged[i]) {
+					HeapGarbageCollectMark(context, c->localVariables[i].i);
+				}
 			}
+
+			for (uintptr_t i = 0; i < c->stackPointer; i++) {
+				if (c->stackIsManaged[i]) {
+					HeapGarbageCollectMark(context, c->stack[i].i);
+				}
+			}
+
+			c = c->nextCoroutine;
 		}
 		
-		for (uintptr_t i = 0; i < context->stackPointer; i++) {
-			if (context->stackIsManaged[i]) {
-				HeapGarbageCollectMark(context, context->stack[i].i);
-			}
-		}
-
 		uintptr_t *link = &context->heapFirstUnusedEntry;
 		uintptr_t reclaimed = 0;
 
@@ -3033,62 +3090,62 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 	// 	Checking that this is actually a valid function body pointer.
 	// 	Checking various integer overflows.
 
-	uintptr_t variableBase = context->localVariableCount - 1;
+	uintptr_t variableBase = context->c->localVariableCount - 1;
 	uint8_t *functionData = context->functionData->data;
 
 	while (true) {
 		uint8_t command = functionData[instructionPointer++];
-		// PrintDebug("--> %d\n", command);
+		// PrintDebug("--> %d, %ld, %d\n", command, context->c->id);
 
 		if (command == T_BLOCK || command == T_FUNCBODY) {
 			uint16_t newVariableCount = functionData[instructionPointer + 0] + (functionData[instructionPointer + 1] << 8); 
 			instructionPointer += 2;
 
-			if (context->localVariableCount + newVariableCount > context->localVariablesAllocated) {
+			if (context->c->localVariableCount + newVariableCount > context->c->localVariablesAllocated) {
 				// TODO Handling memory errors here.
-				context->localVariablesAllocated = context->localVariableCount + newVariableCount;
-				context->localVariables = AllocateResize(context->localVariables, context->localVariablesAllocated * sizeof(Value)); 
-				context->localVariableIsManaged = AllocateResize(context->localVariableIsManaged, context->localVariablesAllocated * sizeof(bool)); 
+				context->c->localVariablesAllocated = context->c->localVariableCount + newVariableCount;
+				context->c->localVariables = AllocateResize(context->c->localVariables, context->c->localVariablesAllocated * sizeof(Value)); 
+				context->c->localVariableIsManaged = AllocateResize(context->c->localVariableIsManaged, context->c->localVariablesAllocated * sizeof(bool)); 
 			}
 
-			MemoryCopy(context->localVariableIsManaged + context->localVariableCount, functionData + instructionPointer, newVariableCount);
+			MemoryCopy(context->c->localVariableIsManaged + context->c->localVariableCount, functionData + instructionPointer, newVariableCount);
 			instructionPointer += newVariableCount;
 
-			for (uintptr_t i = context->localVariableCount; i < context->localVariableCount + newVariableCount; i++) {
+			for (uintptr_t i = context->c->localVariableCount; i < context->c->localVariableCount + newVariableCount; i++) {
 				if (command == T_FUNCBODY) {
-					if (context->stackPointer < 1) return -1;
-					context->localVariables[i] = context->stack[--context->stackPointer];
+					if (context->c->stackPointer < 1) return -1;
+					context->c->localVariables[i] = context->c->stack[--context->c->stackPointer];
 				} else {
 					Value zero = { 0 };
-					context->localVariables[i] = zero;
+					context->c->localVariables[i] = zero;
 				}
 			}
 
-			context->localVariableCount += newVariableCount;
+			context->c->localVariableCount += newVariableCount;
 		} else if (command == T_EXIT_SCOPE) {
 			uint16_t count = functionData[instructionPointer + 0] + (functionData[instructionPointer + 1] << 8); 
 			instructionPointer += 2;
-			if (context->localVariableCount < count) return -1;
-			context->localVariableCount -= count;
+			if (context->c->localVariableCount < count) return -1;
+			context->c->localVariableCount -= count;
 		} else if (command == T_NUMERIC_LITERAL) {
-			if (context->stackPointer == context->stackEntriesAllocated) {
+			if (context->c->stackPointer == context->c->stackEntriesAllocated) {
 				PrintError4(context, instructionPointer - 1, "Stack overflow.\n");
 				return 0;
 			}
 
-			context->stackIsManaged[context->stackPointer] = false;
-			MemoryCopy(&context->stack[context->stackPointer++], &functionData[instructionPointer], sizeof(Value));
+			context->c->stackIsManaged[context->c->stackPointer] = false;
+			MemoryCopy(&context->c->stack[context->c->stackPointer++], &functionData[instructionPointer], sizeof(Value));
 			instructionPointer += sizeof(Value);
 		} else if (command == T_NULL) {
-			if (context->stackPointer == context->stackEntriesAllocated) {
+			if (context->c->stackPointer == context->c->stackEntriesAllocated) {
 				PrintError4(context, instructionPointer - 1, "Stack overflow.\n");
 				return 0;
 			}
 
-			context->stackIsManaged[context->stackPointer] = true;
-			context->stack[context->stackPointer++].i = 0;
+			context->c->stackIsManaged[context->c->stackPointer] = true;
+			context->c->stack[context->c->stackPointer++].i = 0;
 		} else if (command == T_STRING_LITERAL) {
-			if (context->stackPointer == context->stackEntriesAllocated) {
+			if (context->c->stackPointer == context->c->stackEntriesAllocated) {
 				PrintError4(context, instructionPointer - 1, "Stack overflow.\n");
 				return 0;
 			}
@@ -3107,21 +3164,21 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 
 			Value v;
 			v.i = index;
-			context->stackIsManaged[context->stackPointer] = true;
-			context->stack[context->stackPointer++] = v;
+			context->c->stackIsManaged[context->c->stackPointer] = true;
+			context->c->stack[context->c->stackPointer++] = v;
 		} else if (command == T_CONCAT) {
-			if (context->stackPointer < 2) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 2]) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 1]) return -1;
+			if (context->c->stackPointer < 2) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
 
-			uint64_t index1 = context->stack[context->stackPointer - 2].i;
+			uint64_t index1 = context->c->stack[context->c->stackPointer - 2].i;
 			if (context->heapEntriesAllocated <= index1) return -1;
 			HeapEntry *entry1 = &context->heap[index1];
 			if (entry1->type != T_EOF && entry1->type != T_STR) return -1;
 			const char *text1 = entry1->type == T_STR ? entry1->text : "";
 			size_t bytes1 = entry1->type == T_STR ? entry1->bytes : 0;
 
-			uint64_t index2 = context->stack[context->stackPointer - 1].i;
+			uint64_t index2 = context->c->stack[context->c->stackPointer - 1].i;
 			if (context->heapEntriesAllocated <= index2) return -1;
 			HeapEntry *entry2 = &context->heap[index2];
 			if (entry2->type != T_EOF && entry2->type != T_STR) return -1;
@@ -3135,16 +3192,16 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			context->heap[index].text = (char *) AllocateResize(NULL, context->heap[index].bytes);
 			if (bytes1) MemoryCopy(context->heap[index].text + 0,      text1, bytes1);
 			if (bytes2) MemoryCopy(context->heap[index].text + bytes1, text2, bytes2);
-			context->stack[context->stackPointer - 2].i = index;
+			context->c->stack[context->c->stackPointer - 2].i = index;
 
-			context->stackPointer--;
+			context->c->stackPointer--;
 		} else if (command == T_INTERPOLATE_STR || command == T_INTERPOLATE_BOOL 
 				|| command == T_INTERPOLATE_INT || command == T_INTERPOLATE_FLOAT) {
-			if (context->stackPointer < 3) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 3]) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 1]) return -1;
+			if (context->c->stackPointer < 3) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 3]) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
 
-			uint64_t index1 = context->stack[context->stackPointer - 3].i;
+			uint64_t index1 = context->c->stack[context->c->stackPointer - 3].i;
 			if (context->heapEntriesAllocated <= index1) return -1;
 			HeapEntry *entry1 = &context->heap[index1];
 			if (entry1->type != T_EOF && entry1->type != T_STR) return -1;
@@ -3156,25 +3213,25 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			char temp[30];
 
 			if (command == T_INTERPOLATE_STR) {
-				if (!context->stackIsManaged[context->stackPointer - 2]) return -1;
-				uint64_t index2 = context->stack[context->stackPointer - 2].i;
+				if (!context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
+				uint64_t index2 = context->c->stack[context->c->stackPointer - 2].i;
 				if (context->heapEntriesAllocated <= index2) return -1;
 				HeapEntry *entry2 = &context->heap[index2];
 				if (entry2->type != T_EOF && entry2->type != T_STR) return -1;
 				text2 = entry2->type == T_STR ? entry2->text : "";
 				bytes2 = entry2->type == T_STR ? entry2->bytes : 0;
 			} else if (command == T_INTERPOLATE_BOOL) {
-				text2 = context->stack[context->stackPointer - 2].i ? "true" : "false";
-				bytes2 = context->stack[context->stackPointer - 2].i ? 4 : 5;
+				text2 = context->c->stack[context->c->stackPointer - 2].i ? "true" : "false";
+				bytes2 = context->c->stack[context->c->stackPointer - 2].i ? 4 : 5;
 			} else if (command == T_INTERPOLATE_INT) {
 				text2 = temp;
-				bytes2 = PrintIntegerToBuffer(temp, sizeof(temp), context->stack[context->stackPointer - 2].i);
+				bytes2 = PrintIntegerToBuffer(temp, sizeof(temp), context->c->stack[context->c->stackPointer - 2].i);
 			} else if (command == T_INTERPOLATE_FLOAT) {
 				text2 = temp;
-				bytes2 = PrintFloatToBuffer(temp, sizeof(temp), context->stack[context->stackPointer - 2].f);
+				bytes2 = PrintFloatToBuffer(temp, sizeof(temp), context->c->stack[context->c->stackPointer - 2].f);
 			}
 
-			uint64_t index3 = context->stack[context->stackPointer - 1].i;
+			uint64_t index3 = context->c->stack[context->c->stackPointer - 1].i;
 			if (context->heapEntriesAllocated <= index3) return -1;
 			HeapEntry *entry3 = &context->heap[index3];
 			if (entry3->type != T_EOF && entry3->type != T_STR) return -1;
@@ -3189,11 +3246,11 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			if (bytes1) MemoryCopy(context->heap[index].text + 0,               text1, bytes1);
 			if (bytes2) MemoryCopy(context->heap[index].text + bytes1,          text2, bytes2);
 			if (bytes3) MemoryCopy(context->heap[index].text + bytes1 + bytes2, text3, bytes3);
-			context->stack[context->stackPointer - 3].i = index;
+			context->c->stack[context->c->stackPointer - 3].i = index;
 
-			context->stackPointer -= 2;
+			context->c->stackPointer -= 2;
 		} else if (command == T_VARIABLE) {
-			if (context->stackPointer == context->stackEntriesAllocated) {
+			if (context->c->stackPointer == context->c->stackEntriesAllocated) {
 				PrintDebug("Stack overflow.\n");
 				return -1;
 			}
@@ -3204,35 +3261,35 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 
 			if (scopeIndex >= 0) {
 				if ((uintptr_t) scopeIndex >= context->globalVariableCount) return -1;
-				context->stackIsManaged[context->stackPointer] = context->globalVariableIsManaged[scopeIndex];
-				context->stack[context->stackPointer++] = context->globalVariables[scopeIndex];
+				context->c->stackIsManaged[context->c->stackPointer] = context->globalVariableIsManaged[scopeIndex];
+				context->c->stack[context->c->stackPointer++] = context->globalVariables[scopeIndex];
 			} else {
 				scopeIndex = variableBase - scopeIndex;
-				if ((uintptr_t) scopeIndex >= context->localVariableCount) return -1;
-				context->stackIsManaged[context->stackPointer] = context->localVariableIsManaged[scopeIndex];
-				context->stack[context->stackPointer++] = context->localVariables[scopeIndex];
+				if ((uintptr_t) scopeIndex >= context->c->localVariableCount) return -1;
+				context->c->stackIsManaged[context->c->stackPointer] = context->c->localVariableIsManaged[scopeIndex];
+				context->c->stack[context->c->stackPointer++] = context->c->localVariables[scopeIndex];
 			}
 		} else if (command == T_EQUALS) {
-			if (!context->stackPointer) return -1;
+			if (!context->c->stackPointer) return -1;
 			int32_t scopeIndex;
 			MemoryCopy(&scopeIndex, &functionData[instructionPointer], sizeof(scopeIndex));
 			instructionPointer += sizeof(scopeIndex);
 
 			if (scopeIndex >= 0) {
 				if ((uintptr_t) scopeIndex >= context->globalVariableCount) return -1;
-				if (context->globalVariableIsManaged[scopeIndex] != context->stackIsManaged[context->stackPointer - 1]) return -1;
-				context->globalVariables[scopeIndex] = context->stack[--context->stackPointer];
+				if (context->globalVariableIsManaged[scopeIndex] != context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+				context->globalVariables[scopeIndex] = context->c->stack[--context->c->stackPointer];
 			} else {
 				scopeIndex = variableBase - scopeIndex;
-				if ((uintptr_t) scopeIndex >= context->localVariableCount) return -1;
-				if (context->localVariableIsManaged[scopeIndex] != context->stackIsManaged[context->stackPointer - 1]) return -1;
-				context->localVariables[scopeIndex] = context->stack[--context->stackPointer];
+				if ((uintptr_t) scopeIndex >= context->c->localVariableCount) return -1;
+				if (context->c->localVariableIsManaged[scopeIndex] != context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+				context->c->localVariables[scopeIndex] = context->c->stack[--context->c->stackPointer];
 			}
 		} else if (command == T_EQUALS_DOT) {
-			if (context->stackPointer < 2) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 1]) return -1;
+			if (context->c->stackPointer < 2) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
 
-			uint64_t index = context->stack[context->stackPointer - 1].i;
+			uint64_t index = context->c->stack[context->c->stackPointer - 1].i;
 
 			if (!index) {
 				PrintError4(context, instructionPointer - 1, "The struct is null.\n");
@@ -3250,17 +3307,17 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			if (isManaged) fieldIndex = -fieldIndex - 1;
 			if (fieldIndex < 0 || fieldIndex >= entry->fieldCount) return -1;
 
-			entry->fields[fieldIndex] = context->stack[context->stackPointer - 2];
-			if (isManaged != context->stackIsManaged[context->stackPointer - 2]) return -1;
+			entry->fields[fieldIndex] = context->c->stack[context->c->stackPointer - 2];
+			if (isManaged != context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
 			((uint8_t *) entry->fields - 1)[-fieldIndex] = isManaged;
 
-			context->stackPointer -= 2;
+			context->c->stackPointer -= 2;
 		} else if (command == T_EQUALS_LIST) {
-			if (context->stackPointer < 3) return -1;
-			if (context->stackIsManaged[context->stackPointer - 1]) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 2]) return -1;
+			if (context->c->stackPointer < 3) return -1;
+			if (context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
 
-			uint64_t index = context->stack[context->stackPointer - 2].i;
+			uint64_t index = context->c->stack[context->c->stackPointer - 2].i;
 
 			if (!index) {
 				PrintError4(context, instructionPointer - 1, "The list is null.\n");
@@ -3271,23 +3328,23 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			HeapEntry *entry = &context->heap[index];
 			if (entry->type != T_LIST) return -1;
 
-			index = context->stack[context->stackPointer - 1].i;
+			index = context->c->stack[context->c->stackPointer - 1].i;
 
 			if (index >= entry->length) {
 				PrintError4(context, instructionPointer - 1, "The index %ld is not valid for the list, which has length %d.\n", index, entry->length);
 				return 0;
 			}
 
-			entry->list[index] = context->stack[context->stackPointer - 3];
-			if (entry->internalValuesAreManaged != context->stackIsManaged[context->stackPointer - 3]) return -1;
+			entry->list[index] = context->c->stack[context->c->stackPointer - 3];
+			if (entry->internalValuesAreManaged != context->c->stackIsManaged[context->c->stackPointer - 3]) return -1;
 
-			context->stackPointer -= 3;
+			context->c->stackPointer -= 3;
 		} else if (command == T_INDEX_LIST) {
-			if (context->stackPointer < 2) return -1;
-			if (context->stackIsManaged[context->stackPointer - 1]) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 2]) return -1;
+			if (context->c->stackPointer < 2) return -1;
+			if (context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
 
-			uint64_t index = context->stack[context->stackPointer - 2].i;
+			uint64_t index = context->c->stack[context->c->stackPointer - 2].i;
 
 			if (!index) {
 				PrintError4(context, instructionPointer - 1, "The list is null.\n");
@@ -3298,21 +3355,21 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			HeapEntry *entry = &context->heap[index];
 			if (entry->type != T_LIST) return -1;
 
-			index = context->stack[context->stackPointer - 1].i;
+			index = context->c->stack[context->c->stackPointer - 1].i;
 
 			if (index >= entry->length) {
 				PrintError4(context, instructionPointer - 1, "The index %ld is not valid for the list, which has length %d.\n", index, entry->length);
 				return 0;
 			}
 
-			context->stack[context->stackPointer - 2] = entry->list[index];
-			context->stackIsManaged[context->stackPointer - 2] = entry->internalValuesAreManaged;
-			context->stackPointer--;
+			context->c->stack[context->c->stackPointer - 2] = entry->list[index];
+			context->c->stackIsManaged[context->c->stackPointer - 2] = entry->internalValuesAreManaged;
+			context->c->stackPointer--;
 		} else if (command == T_OP_FIRST || command == T_OP_LAST) {
-			if (context->stackPointer < 1) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 1]) return -1;
+			if (context->c->stackPointer < 1) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
 
-			uint64_t index = context->stack[context->stackPointer - 1].i;
+			uint64_t index = context->c->stack[context->c->stackPointer - 1].i;
 
 			if (!index) {
 				PrintError4(context, instructionPointer - 1, "The list is null.\n");
@@ -3328,13 +3385,13 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 				return 0;
 			}
 
-			context->stack[context->stackPointer - 1] = entry->list[command == T_OP_FIRST ? 0 : entry->length - 1];
-			context->stackIsManaged[context->stackPointer - 1] = entry->internalValuesAreManaged;
+			context->c->stack[context->c->stackPointer - 1] = entry->list[command == T_OP_FIRST ? 0 : entry->length - 1];
+			context->c->stackIsManaged[context->c->stackPointer - 1] = entry->internalValuesAreManaged;
 		} else if (command == T_DOT) {
-			if (context->stackPointer < 1) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 1]) return -1;
+			if (context->c->stackPointer < 1) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
 
-			uint64_t index = context->stack[context->stackPointer - 1].i;
+			uint64_t index = context->c->stack[context->c->stackPointer - 1].i;
 
 			if (!index) {
 				PrintError4(context, instructionPointer - 1, "The struct is null.\n");
@@ -3355,110 +3412,110 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			// Only allow the isManaged bool to be incorrect if it's a null managed variable.
 			if (isManaged != ((uint8_t *) entry->fields - 1)[-fieldIndex] && (entry->fields[fieldIndex].i || !isManaged)) return -1;
 
-			context->stack[context->stackPointer - 1] = entry->fields[fieldIndex];
-			context->stackIsManaged[context->stackPointer - 1] = isManaged;
+			context->c->stack[context->c->stackPointer - 1] = entry->fields[fieldIndex];
+			context->c->stackIsManaged[context->c->stackPointer - 1] = isManaged;
 		} else if (command == T_ADD) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].i + context->stack[context->stackPointer - 1].i;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].i + context->c->stack[context->c->stackPointer - 1].i;
+			context->c->stackPointer--;
 		} else if (command == T_MINUS) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].i - context->stack[context->stackPointer - 1].i;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].i - context->c->stack[context->c->stackPointer - 1].i;
+			context->c->stackPointer--;
 		} else if (command == T_ASTERISK) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].i * context->stack[context->stackPointer - 1].i;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].i * context->c->stack[context->c->stackPointer - 1].i;
+			context->c->stackPointer--;
 		} else if (command == T_SLASH) {
-			if (context->stackPointer < 2) return -1;
+			if (context->c->stackPointer < 2) return -1;
 
-			if (0 == context->stack[context->stackPointer - 1].i) {
+			if (0 == context->c->stack[context->c->stackPointer - 1].i) {
 				PrintError4(context, instructionPointer - 1, "Attempted division by zero.\n");
 				return 0;
 			}
 
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].f / context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].f / context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_FLOAT_ADD) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].f = context->stack[context->stackPointer - 2].f + context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].f = context->c->stack[context->c->stackPointer - 2].f + context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_FLOAT_MINUS) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].f = context->stack[context->stackPointer - 2].f - context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].f = context->c->stack[context->c->stackPointer - 2].f - context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_FLOAT_ASTERISK) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].f = context->stack[context->stackPointer - 2].f * context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].f = context->c->stack[context->c->stackPointer - 2].f * context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_FLOAT_SLASH) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].f = context->stack[context->stackPointer - 2].f / context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].f = context->c->stack[context->c->stackPointer - 2].f / context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_LESS_THAN) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].i < context->stack[context->stackPointer - 1].i;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].i < context->c->stack[context->c->stackPointer - 1].i;
+			context->c->stackPointer--;
 		} else if (command == T_GREATER_THAN) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].i > context->stack[context->stackPointer - 1].i;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].i > context->c->stack[context->c->stackPointer - 1].i;
+			context->c->stackPointer--;
 		} else if (command == T_LT_OR_EQUAL) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].i <= context->stack[context->stackPointer - 1].i;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].i <= context->c->stack[context->c->stackPointer - 1].i;
+			context->c->stackPointer--;
 		} else if (command == T_GT_OR_EQUAL) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].i >= context->stack[context->stackPointer - 1].i;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].i >= context->c->stack[context->c->stackPointer - 1].i;
+			context->c->stackPointer--;
 		} else if (command == T_DOUBLE_EQUALS) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].i == context->stack[context->stackPointer - 1].i;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].i == context->c->stack[context->c->stackPointer - 1].i;
+			context->c->stackPointer--;
 		} else if (command == T_NOT_EQUALS) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].i != context->stack[context->stackPointer - 1].i;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].i != context->c->stack[context->c->stackPointer - 1].i;
+			context->c->stackPointer--;
 		} else if (command == T_LOGICAL_NOT) {
-			if (context->stackPointer < 1) return -1;
-			context->stack[context->stackPointer - 1].i = !context->stack[context->stackPointer - 1].i;
+			if (context->c->stackPointer < 1) return -1;
+			context->c->stack[context->c->stackPointer - 1].i = !context->c->stack[context->c->stackPointer - 1].i;
 		} else if (command == T_FLOAT_LESS_THAN) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].f < context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].f < context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_FLOAT_GREATER_THAN) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].f > context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].f > context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_FLOAT_LT_OR_EQUAL) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].f <= context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].f <= context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_FLOAT_GT_OR_EQUAL) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].f >= context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].f >= context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_FLOAT_DOUBLE_EQUALS) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].f == context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].f == context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_FLOAT_NOT_EQUALS) {
-			if (context->stackPointer < 2) return -1;
-			context->stack[context->stackPointer - 2].i = context->stack[context->stackPointer - 2].f != context->stack[context->stackPointer - 1].f;
-			context->stackPointer--;
+			if (context->c->stackPointer < 2) return -1;
+			context->c->stack[context->c->stackPointer - 2].i = context->c->stack[context->c->stackPointer - 2].f != context->c->stack[context->c->stackPointer - 1].f;
+			context->c->stackPointer--;
 		} else if (command == T_STR_DOUBLE_EQUALS || command == T_STR_NOT_EQUALS) {
-			if (context->stackPointer < 2) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 2]) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 1]) return -1;
+			if (context->c->stackPointer < 2) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
 
-			uint64_t index1 = context->stack[context->stackPointer - 2].i;
+			uint64_t index1 = context->c->stack[context->c->stackPointer - 2].i;
 			if (context->heapEntriesAllocated <= index1) return -1;
 			HeapEntry *entry1 = &context->heap[index1];
 			if (entry1->type != T_EOF && entry1->type != T_STR) return -1;
 			const char *text1 = entry1->type == T_STR ? entry1->text : 0;
 			size_t bytes1 = entry1->type == T_STR ? entry1->bytes : 0;
 
-			uint64_t index2 = context->stack[context->stackPointer - 1].i;
+			uint64_t index2 = context->c->stack[context->c->stackPointer - 1].i;
 			if (context->heapEntriesAllocated <= index2) return -1;
 			HeapEntry *entry2 = &context->heap[index2];
 			if (entry2->type != T_EOF && entry2->type != T_STR) return -1;
@@ -3467,14 +3524,14 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 
 			bool equal = bytes1 == bytes2 && 0 == MemoryCompare(text1, text2, bytes1);
 
-			context->stack[context->stackPointer - 2].i = command == T_STR_NOT_EQUALS ? !equal : equal;
-			context->stackIsManaged[context->stackPointer - 2] = false;
+			context->c->stack[context->c->stackPointer - 2].i = command == T_STR_NOT_EQUALS ? !equal : equal;
+			context->c->stackIsManaged[context->c->stackPointer - 2] = false;
 
-			context->stackPointer--;
+			context->c->stackPointer--;
 		} else if (command == T_OP_LEN) {
-			if (context->stackPointer < 1) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 1]) return -1;
-			uint64_t index = context->stack[context->stackPointer - 1].i;
+			if (context->c->stackPointer < 1) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+			uint64_t index = context->c->stack[context->c->stackPointer - 1].i;
 			if (context->heapEntriesAllocated <= index) return -1;
 			HeapEntry *entry = &context->heap[index];
 			int64_t length;
@@ -3482,17 +3539,17 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			else if (entry->type == T_STR) length = entry->bytes;
 			else if (entry->type == T_LIST) length = entry->length;
 			else return -1;
-			context->stack[context->stackPointer - 1].i = length;
-			context->stackIsManaged[context->stackPointer - 1] = false;
+			context->c->stack[context->c->stackPointer - 1].i = length;
+			context->c->stackIsManaged[context->c->stackPointer - 1] = false;
 		} else if (command == T_INDEX) {
-			if (context->stackPointer < 1) return -1;
-			if (context->stackIsManaged[context->stackPointer - 1]) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 2]) return -1;
-			uint64_t index = context->stack[context->stackPointer - 2].i;
+			if (context->c->stackPointer < 1) return -1;
+			if (context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
+			uint64_t index = context->c->stack[context->c->stackPointer - 2].i;
 			if (context->heapEntriesAllocated <= index) return -1;
 			HeapEntry *entry = &context->heap[index];
 			if (entry->type != T_EOF && entry->type != T_STR) return -1;
-			index = context->stack[context->stackPointer - 1].i;
+			index = context->c->stack[context->c->stackPointer - 1].i;
 			size_t bytes = entry->type == T_STR ? entry->bytes : 0;
 
 			if (index >= bytes) {
@@ -3507,13 +3564,14 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			context->heap[index].bytes = 1;
 			context->heap[index].text = (char *) AllocateResize(NULL, 1); // TODO Handling allocation failure.
 			context->heap[index].text[0] = c;
-			context->stack[context->stackPointer - 2].i = index;
-			context->stackIsManaged[context->stackPointer - 2] = true;
-			context->stackPointer--;
+			context->c->stack[context->c->stackPointer - 2].i = index;
+			context->c->stackIsManaged[context->c->stackPointer - 2] = true;
+			context->c->stackPointer--;
 		} else if (command == T_CALL) {
-			if (context->stackPointer < 1) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 1]) return -1;
-			Value newBody = context->stack[--context->stackPointer];
+			callCommand:;
+			if (context->c->stackPointer < 1) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+			Value newBody = context->c->stack[--context->c->stackPointer];
 
 			if (newBody.i == 0) {
 				PrintError4(context, instructionPointer - 1, "Function pointer was null.\n");
@@ -3534,14 +3592,14 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 				} else if (entry->type == T_OP_ASSERT) {
 					assertResult = true;
 				} else if (entry->type == T_OP_CURRY) {
-					if (context->stackPointer == context->stackEntriesAllocated) {
+					if (context->c->stackPointer == context->c->stackEntriesAllocated) {
 						PrintError4(context, instructionPointer - 1, "Stack overflow.\n");
 						return 0;
 					}
 
-					context->stack[context->stackPointer] = entry->curryValue;
-					context->stackIsManaged[context->stackPointer] = entry->internalValuesAreManaged;
-					context->stackPointer++;
+					context->c->stack[context->c->stackPointer] = entry->curryValue;
+					context->c->stackIsManaged[context->c->stackPointer] = entry->internalValuesAreManaged;
+					context->c->stackPointer++;
 				} else if (entry->type == T_FUNCPTR) {
 					break;
 				} else {
@@ -3549,70 +3607,70 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 				}
 			} 
 
-			if (context->backTracePointer == sizeof(context->backTrace) / sizeof(context->backTrace[0])) {
+			if (context->c->backTracePointer == sizeof(context->c->backTrace) / sizeof(context->c->backTrace[0])) {
 				PrintError4(context, instructionPointer - 1, "Back trace overflow.\n");
 				return 0;
 			}
 			
-			BackTraceItem *link = &context->backTrace[context->backTracePointer];
-			context->backTracePointer++;
+			BackTraceItem *link = &context->c->backTrace[context->c->backTracePointer];
+			context->c->backTracePointer++;
 			link->instructionPointer = instructionPointer;
 			link->variableBase = variableBase;
 			link->popResult = popResult;
 			link->assertResult = assertResult;
 			instructionPointer = newBody.i;
-			variableBase = context->localVariableCount - 1;
+			variableBase = context->c->localVariableCount - 1;
 		} else if (command == T_IF) {
-			if (context->stackPointer < 1) return -1;
-			Value condition = context->stack[--context->stackPointer];
+			if (context->c->stackPointer < 1) return -1;
+			Value condition = context->c->stack[--context->c->stackPointer];
 			int32_t delta;
 			MemoryCopy(&delta, &functionData[instructionPointer], sizeof(delta));
 			instructionPointer += condition.i ? (int32_t) sizeof(delta) : delta; 
 		} else if (command == T_LOGICAL_OR) {
-			if (context->stackPointer < 1) return -1;
-			Value condition = context->stack[context->stackPointer - 1];
+			if (context->c->stackPointer < 1) return -1;
+			Value condition = context->c->stack[context->c->stackPointer - 1];
 			int32_t delta;
 			MemoryCopy(&delta, &functionData[instructionPointer], sizeof(delta));
 			instructionPointer += condition.i ? delta : (int32_t) sizeof(delta); 
-			if (!condition.i) context->stackPointer--;
+			if (!condition.i) context->c->stackPointer--;
 		} else if (command == T_LOGICAL_AND) {
-			if (context->stackPointer < 1) return -1;
-			Value condition = context->stack[context->stackPointer - 1];
+			if (context->c->stackPointer < 1) return -1;
+			Value condition = context->c->stack[context->c->stackPointer - 1];
 			int32_t delta;
 			MemoryCopy(&delta, &functionData[instructionPointer], sizeof(delta));
 			instructionPointer += condition.i ? (int32_t) sizeof(delta) : delta; 
-			if (condition.i) context->stackPointer--;
+			if (condition.i) context->c->stackPointer--;
 		} else if (command == T_BRANCH) {
 			int32_t delta;
 			MemoryCopy(&delta, &functionData[instructionPointer], sizeof(delta));
 			instructionPointer += delta; 
 		} else if (command == T_POP) {
-			if (context->stackPointer < 1) return -1;
-			context->stackPointer--;
+			if (context->c->stackPointer < 1) return -1;
+			context->c->stackPointer--;
 		} else if (command == T_DUP) {
-			if (context->stackPointer < 1) return -1;
+			if (context->c->stackPointer < 1) return -1;
 
-			if (context->stackPointer == context->stackEntriesAllocated) {
+			if (context->c->stackPointer == context->c->stackEntriesAllocated) {
 				PrintError4(context, instructionPointer - 1, "Stack overflow.\n");
 				return 0;
 			}
 
-			context->stack[context->stackPointer] = context->stack[context->stackPointer - 1];
-			context->stackIsManaged[context->stackPointer] = context->stackIsManaged[context->stackPointer - 1];
-			context->stackPointer++;
+			context->c->stack[context->c->stackPointer] = context->c->stack[context->c->stackPointer - 1];
+			context->c->stackIsManaged[context->c->stackPointer] = context->c->stackIsManaged[context->c->stackPointer - 1];
+			context->c->stackPointer++;
 		} else if (command == T_SWAP) {
-			if (context->stackPointer < 2) return -1;
-			Value v1 = context->stack[context->stackPointer - 1];
-			Value v2 = context->stack[context->stackPointer - 2];
-			bool m1 = context->stackIsManaged[context->stackPointer - 1];
-			bool m2 = context->stackIsManaged[context->stackPointer - 2];
-			context->stack[context->stackPointer - 1] = v2;
-			context->stack[context->stackPointer - 2] = v1;
-			context->stackIsManaged[context->stackPointer - 1] = m2;
-			context->stackIsManaged[context->stackPointer - 2] = m1;
+			if (context->c->stackPointer < 2) return -1;
+			Value v1 = context->c->stack[context->c->stackPointer - 1];
+			Value v2 = context->c->stack[context->c->stackPointer - 2];
+			bool m1 = context->c->stackIsManaged[context->c->stackPointer - 1];
+			bool m2 = context->c->stackIsManaged[context->c->stackPointer - 2];
+			context->c->stack[context->c->stackPointer - 1] = v2;
+			context->c->stack[context->c->stackPointer - 2] = v1;
+			context->c->stackIsManaged[context->c->stackPointer - 1] = m2;
+			context->c->stackIsManaged[context->c->stackPointer - 2] = m1;
 		} else if (command == T_ASSERT) {
-			if (context->stackPointer < 1) return -1;
-			Value condition = context->stack[--context->stackPointer];
+			if (context->c->stackPointer < 1) return -1;
+			Value condition = context->c->stack[--context->c->stackPointer];
 
 			if (condition.i == 0) {
 				PrintError4(context, instructionPointer - 1, "Assertion failed.\n");
@@ -3623,7 +3681,7 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 				return 0;
 			}
 		} else if (command == T_NEW) {
-			if (context->stackPointer == context->stackEntriesAllocated) {
+			if (context->c->stackPointer == context->c->stackEntriesAllocated) {
 				PrintError4(context, instructionPointer - 1, "Stack overflow.\n");
 				return 0;
 			}
@@ -3652,13 +3710,13 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 
 			Value v;
 			v.i = index;
-			context->stackIsManaged[context->stackPointer] = true;
-			context->stack[context->stackPointer++] = v;
+			context->c->stackIsManaged[context->c->stackPointer] = true;
+			context->c->stack[context->c->stackPointer++] = v;
 		} else if (command == T_OP_RESIZE) {
-			if (context->stackPointer < 2) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 2]) return -1;
+			if (context->c->stackPointer < 2) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
 
-			uint64_t index = context->stack[context->stackPointer - 2].i;
+			uint64_t index = context->c->stack[context->c->stackPointer - 2].i;
 
 			if (!index) {
 				PrintError4(context, instructionPointer - 1, "The list is null.\n");
@@ -3669,7 +3727,7 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			HeapEntry *entry = &context->heap[index];
 			if (entry->type != T_LIST) return -1;
 
-			int64_t newLength = context->stack[context->stackPointer - 1].i;
+			int64_t newLength = context->c->stack[context->c->stackPointer - 1].i;
 
 			if (newLength < 0 || newLength >= 1000000000) {
 				PrintError4(context, instructionPointer - 1, "The new length of the list is out of the supported range (0..1000000000).\n");
@@ -3687,34 +3745,242 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 				context->heap[index].list[i].i = 0;
 			}
 
-			context->stackPointer -= 2;
+			context->c->stackPointer -= 2;
+		} else if (command == T_OP_ADD) {
+			if (context->c->stackPointer < 2) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
+
+			uint64_t index = context->c->stack[context->c->stackPointer - 2].i;
+
+			if (!index) {
+				PrintError4(context, instructionPointer - 1, "The list is null.\n");
+				return 0;
+			}
+
+			if (context->heapEntriesAllocated <= index) return -1;
+			HeapEntry *entry = &context->heap[index];
+			if (entry->type != T_LIST) return -1;
+
+			int64_t newLength = entry->length + 1;
+
+			if (newLength < 0 || newLength >= 1000000000) {
+				PrintError4(context, instructionPointer - 1, "The new length of the list is out of the supported range (0..1000000000).\n");
+				return 0;
+			}
+
+			uint32_t oldLength = context->heap[index].length;
+			entry->length = newLength;
+
+			if (entry->length > entry->allocated) {
+				// TODO Handling out of memory errors.
+				entry->allocated = entry->allocated ? entry->allocated * 2 : 4;
+				entry->list = AllocateResize(entry->list, entry->allocated * sizeof(Value));
+				Assert(entry->length <= entry->allocated);
+			}
+
+			if (entry->internalValuesAreManaged != context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+			entry->list[oldLength] = context->c->stack[context->c->stackPointer - 1];
+
+			context->c->stackPointer -= 2;
+		} else if (command == T_OP_FIND_AND_DELETE) {
+			if (context->c->stackPointer < 2) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
+
+			uint64_t index = context->c->stack[context->c->stackPointer - 2].i;
+
+			if (!index) {
+				PrintError4(context, instructionPointer - 1, "The list is null.\n");
+				return 0;
+			}
+
+			if (context->heapEntriesAllocated <= index) return -1;
+			HeapEntry *entry = &context->heap[index];
+			if (entry->type != T_LIST) return -1;
+			if (entry->internalValuesAreManaged != context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+			bool found = false;
+
+			for (uintptr_t i = 0; i < entry->length; i++) {
+				if (entry->list[i].i == context->c->stack[context->c->stackPointer - 1].i) {
+					entry->length--;
+					found = true;
+
+					for (uintptr_t j = i; j < entry->length; j++) {
+						entry->list[j] = entry->list[j + 1];
+					}
+
+					break;
+				}
+			}
+
+			context->c->stack[context->c->stackPointer - 2].i = found;
+			context->c->stackIsManaged[context->c->stackPointer - 2] = false;
+			context->c->stackPointer--;
 		} else if (command == T_OP_DISCARD || command == T_OP_ASSERT) {
-			if (context->stackPointer < 1) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 1]) return -1;
-			int64_t id = context->stack[context->stackPointer - 1].i;
+			if (context->c->stackPointer < 1) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+			int64_t id = context->c->stack[context->c->stackPointer - 1].i;
 			uintptr_t index = HeapAllocate(context);
 			context->heap[index].type = command;
 			context->heap[index].lambdaID = id;
-			context->stackIsManaged[context->stackPointer - 1] = true;
-			context->stack[context->stackPointer - 1].i = index;
+			context->c->stackIsManaged[context->c->stackPointer - 1] = true;
+			context->c->stack[context->c->stackPointer - 1].i = index;
 		} else if (command == T_OP_CURRY) {
-			if (context->stackPointer < 2) return -1;
-			if (!context->stackIsManaged[context->stackPointer - 2]) return -1;
-			bool valueIsManaged = context->stackIsManaged[context->stackPointer - 1];
-			Value value = context->stack[context->stackPointer - 1];
-			int64_t id = context->stack[context->stackPointer - 2].i;
+			if (context->c->stackPointer < 2) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 2]) return -1;
+			bool valueIsManaged = context->c->stackIsManaged[context->c->stackPointer - 1];
+			Value value = context->c->stack[context->c->stackPointer - 1];
+			int64_t id = context->c->stack[context->c->stackPointer - 2].i;
 			uintptr_t index = HeapAllocate(context);
 			context->heap[index].type = command;
 			context->heap[index].lambdaID = id;
 			context->heap[index].curryValue = value;
 			context->heap[index].internalValuesAreManaged = valueIsManaged;
-			context->stackIsManaged[context->stackPointer - 2] = true;
-			context->stack[context->stackPointer - 2].i = index;
-			context->stackPointer--;
+			context->c->stackIsManaged[context->c->stackPointer - 2] = true;
+			context->c->stack[context->c->stackPointer - 2].i = index;
+			context->c->stackPointer--;
 		} else if (command == T_OP_ASYNC) {
-			// TODO.
-			context->stackIsManaged[context->stackPointer - 1] = false;
-			context->stack[context->stackPointer - 1].i = -1;
+			if (context->c->stackPointer < 1) return -1;
+			if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+			CoroutineState *c = (CoroutineState *) AllocateResize(NULL, sizeof(CoroutineState)); // TODO Handle allocation failure.
+			CoroutineState empty = { 0 };
+			*c = empty;
+			c->id = ++context->lastCoroutineID;
+			c->startedByAsync = true;
+			c->stackEntriesAllocated = sizeof(context->c->stack) / sizeof(context->c->stack[0]);
+			c->stackPointer = 2;
+			c->stack[0].i = -1; // Indicates to T_AWAIT to remove the coroutine.
+			c->stackIsManaged[0] = false;
+			c->stack[1] = context->c->stack[context->c->stackPointer - 1];
+			c->stackIsManaged[1] = true;
+			c->nextCoroutine = context->allCoroutines;
+			if (c->nextCoroutine) c->nextCoroutine->previousCoroutineLink = &c->nextCoroutine;
+			c->previousCoroutineLink = &context->allCoroutines;
+			context->allCoroutines = c;
+			c->nextUnblockedCoroutine = context->unblockedCoroutines;
+			if (c->nextUnblockedCoroutine) c->nextUnblockedCoroutine->previousUnblockedCoroutineLink = &c->nextUnblockedCoroutine;
+			c->previousUnblockedCoroutineLink = &context->unblockedCoroutines;
+			context->unblockedCoroutines = c;
+			context->c->stackIsManaged[context->c->stackPointer - 1] = false;
+			context->c->stack[context->c->stackPointer - 1].i = c->id;
+		} else if (command == T_AWAIT) {
+			if (context->c->stackPointer < 1) return -1;
+
+			// PrintDebug("== AWAIT from %ld\n", context->c->id);
+			Assert(!context->c->nextUnblockedCoroutine && !context->c->previousUnblockedCoroutineLink);
+
+			if (context->c->stack[context->c->stackPointer - 1].i == -1) {
+				if (context->c->stackPointer != 1) return -1;
+				// The coroutine has finished. Remove it from the list of all coroutines.
+				*context->c->previousCoroutineLink = context->c->nextCoroutine;
+				if (context->c->nextCoroutine) context->c->nextCoroutine->previousCoroutineLink = context->c->previousCoroutineLink;
+
+				// PrintDebug("== finished\n");
+
+				for (uintptr_t i = 0; i < context->c->waiterCount; i++) {
+					CoroutineState *c = context->c->waiters[i];
+					if (!c) continue;
+					Assert(!c->nextUnblockedCoroutine && !c->previousUnblockedCoroutineLink);
+					c->unblockedBy = context->c->id;
+					c->nextUnblockedCoroutine = context->unblockedCoroutines;
+					if (c->nextUnblockedCoroutine) c->nextUnblockedCoroutine->previousUnblockedCoroutineLink = &c->nextUnblockedCoroutine;
+					c->previousUnblockedCoroutineLink = &context->unblockedCoroutines;
+					context->unblockedCoroutines = c;
+
+					for (uintptr_t j = 0; j < c->waitingOnCount; j++) {
+						Assert(*(c->waitingOn[j]) == c);
+						*(c->waitingOn[j]) = NULL;
+					}
+
+					c->waitingOnCount = 0;
+					// PrintDebug("== unblocked %ld\n", c->id);
+				}
+
+				ScriptFreeCoroutine(context->c);
+			} else {
+				if (!context->c->stackIsManaged[context->c->stackPointer - 1]) return -1;
+				// The coroutine is waiting.
+				context->c->unblockedBy = -1;
+				context->c->awaiting = true;
+				context->c->instructionPointer = instructionPointer;
+				context->c->variableBase = variableBase;
+				uint64_t index = context->c->stack[context->c->stackPointer - 1].i;
+				if (context->heapEntriesAllocated <= index) return -1;
+				HeapEntry *entry = &context->heap[index];
+				if (entry->internalValuesAreManaged || entry->type != T_LIST) return -1;
+				
+				context->c->waitingOn = (CoroutineState ***) AllocateResize(context->c->waitingOn, sizeof(CoroutineState **) * entry->length);
+				Assert(context->c->waitingOnCount == 0);
+				CoroutineState *c = context->allCoroutines;
+
+				while (c) {
+					for (uintptr_t i = 0; i < entry->length; i++) {
+						if (c->id == (uint64_t) entry->list[i].i) {
+							if (c->waiterCount == c->waitersAllocated) {
+								c->waitersAllocated = c->waitersAllocated ? c->waitersAllocated * 2 : 4;
+								c->waiters = (CoroutineState **) AllocateResize(c->waiters, sizeof(CoroutineState *) * c->waitersAllocated);
+							}
+
+							c->waiters[c->waiterCount] = context->c;
+							context->c->waitingOn[context->c->waitingOnCount++] = &c->waiters[c->waiterCount];
+							c->waiterCount++;
+						}
+					}
+
+					c = c->nextCoroutine;
+				}
+
+				// PrintDebug("== waiting on %d...\n", context->c->waitingOnCount);
+
+				if (!context->c->waitingOnCount) {
+					// PrintDebug("== immediately unblocking\n");
+					context->c->unblockedBy = entry->length ? entry->list[0].i : -1;
+				}
+			}
+
+			CoroutineState *next = context->unblockedCoroutines;
+
+			if (!next) {
+				PrintError4(context, instructionPointer - 1, "No tasks can run if this task (ID %ld) starts waiting.\n", context->c->id);
+				PrintDebug("All tasks:\n");
+				CoroutineState *c = context->allCoroutines;
+
+				while (c) {
+					PrintDebug("\t%ld blocks ", c->id);
+					bool first = true;
+
+					for (uintptr_t i = 0; i < c->waiterCount; i++) {
+						if (!c->waiters[i]) continue;
+						PrintDebug("%s%ld", first ? "" : ", ", c->waiters[i]->id);
+						first = false;
+					}
+
+					PrintDebug("\n");
+					PrintBackTrace(context, c->instructionPointer - 1, c, "\t");
+					c = c->nextCoroutine;
+				}
+
+				return 0;
+			}
+
+			Assert(next->previousUnblockedCoroutineLink);
+			*next->previousUnblockedCoroutineLink = next->nextUnblockedCoroutine;
+			if (next->nextUnblockedCoroutine) next->nextUnblockedCoroutine->previousUnblockedCoroutineLink = next->previousUnblockedCoroutineLink;
+			next->nextUnblockedCoroutine = NULL;
+			next->previousUnblockedCoroutineLink = NULL;
+			context->c = next;
+			// PrintDebug("== switch to %ld\n", next->id);
+
+			if (context->c->awaiting) {
+				context->c->stackIsManaged[context->c->stackPointer - 1] = false;
+				context->c->stack[context->c->stackPointer - 1].i = context->c->unblockedBy;
+				instructionPointer = context->c->instructionPointer;
+				variableBase = context->c->variableBase;
+				// PrintDebug("== unblocked by %ld\n", context->c->unblockedBy);
+			} else {
+				// PrintDebug("== just started\n");
+				instructionPointer--;
+				goto callCommand;
+			}
 		} else if (command == T_END_FUNCTION || command == T_EXTCALL) {
 			if (command == T_EXTCALL) {
 				uint16_t index = functionData[instructionPointer + 0] + (functionData[instructionPointer + 1] << 8); 
@@ -3726,36 +3992,36 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 					if (result <= 0) return result;
 
 					if (result == 2 || result == 3) {
-						if (context->stackPointer == context->stackEntriesAllocated) {
+						if (context->c->stackPointer == context->c->stackEntriesAllocated) {
 							PrintDebug("Evaluation stack overflow.\n");
 							return -1;
 						}
 
-						context->stackIsManaged[context->stackPointer] = result == 3;
-						context->stack[context->stackPointer++] = returnValue;
+						context->c->stackIsManaged[context->c->stackPointer] = result == 3;
+						context->c->stack[context->c->stackPointer++] = returnValue;
 					}
 				} else {
 					return -1;
 				}
 			}
 
-			context->localVariableCount = variableBase + 1;
+			context->c->localVariableCount = variableBase + 1;
 
-			if (context->backTracePointer) {
-				BackTraceItem *item = &context->backTrace[context->backTracePointer - 1];
+			if (context->c->backTracePointer) {
+				BackTraceItem *item = &context->c->backTrace[context->c->backTracePointer - 1];
 
 				if (command == T_EXTCALL) {
-					context->backTracePointer--;
+					context->c->backTracePointer--;
 					instructionPointer = item->instructionPointer;
 					variableBase = item->variableBase;
 				}
 
 				if (item->popResult) {
-					if (context->stackPointer < 1) return -1;
-					context->stackPointer--;
+					if (context->c->stackPointer < 1) return -1;
+					context->c->stackPointer--;
 				} else if (item->assertResult) {
-					if (context->stackPointer < 1) return -1;
-					Value condition = context->stack[--context->stackPointer];
+					if (context->c->stackPointer < 1) return -1;
+					Value condition = context->c->stack[--context->c->stackPointer];
 
 					if (condition.i == 0) {
 						PrintError4(context, instructionPointer - 1, "Return value was false on an asserting function pointer.\n");
@@ -3764,7 +4030,7 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 				}
 
 				if (command != T_EXTCALL) {
-					context->backTracePointer--;
+					context->c->backTracePointer--;
 					instructionPointer = item->instructionPointer;
 					variableBase = item->variableBase;
 				}
@@ -3777,6 +4043,11 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 		}
 	}
 
+	if (context->allCoroutines->nextCoroutine || context->allCoroutines->startedByAsync) {
+		PrintError3("Script ended with unfinished tasks.\n");
+		return false;
+	}
+
 	return true;
 }
 
@@ -3940,7 +4211,7 @@ int ScriptExecute(ExecutionContext *context, ImportData *mainModule) {
 			if (result == 0) {
 				// A runtime error occurred.
 				return 1;
-			} else if (result == -1 || context->stackPointer != 0) {
+			} else if (result == -1 || context->c->stackPointer != 0) {
 				PrintError3("The script was malformed.\n");
 				return 1;
 			}
@@ -3954,7 +4225,7 @@ int ScriptExecute(ExecutionContext *context, ImportData *mainModule) {
 	if (result == 0) {
 		// A runtime error occurred.
 		return 1;
-	} else if (result == -1 || context->stackPointer != 0) {
+	} else if (result == -1 || context->c->stackPointer != 0) {
 		PrintError3("The script was malformed.\n");
 		return 1;
 	}
@@ -3962,6 +4233,14 @@ int ScriptExecute(ExecutionContext *context, ImportData *mainModule) {
 	return 0;
 }
 
+void ScriptFreeCoroutine(CoroutineState *c) {
+	AllocateResize(c->waiters, 0);
+	AllocateResize(c->waitingOn, 0);
+	AllocateResize(c->localVariables, 0);
+	AllocateResize(c->localVariableIsManaged, 0);
+	AllocateResize(c, 0);
+}
+
 void ScriptFree(ExecutionContext *context) {
 	ImportData *module = importedModules;
 
@@ -3980,11 +4259,17 @@ void ScriptFree(ExecutionContext *context) {
 		}
 	}
 
+	CoroutineState *coroutine = context->allCoroutines;
+
+	while (coroutine) {
+		CoroutineState *next = coroutine->nextCoroutine;
+		ScriptFreeCoroutine(coroutine);
+		coroutine = next;
+	}
+
 	AllocateResize(context->heap, 0);
 	AllocateResize(context->globalVariables, 0);
 	AllocateResize(context->globalVariableIsManaged, 0);
-	AllocateResize(context->localVariables, 0);
-	AllocateResize(context->localVariableIsManaged, 0);
 	AllocateResize(context->functionData->lineNumbers, 0);
 	AllocateResize(context->functionData->data, 0);
 	AllocateResize(context->scriptPersistFile, 0);
@@ -4017,9 +4302,9 @@ size_t fixedAllocationCurrentSize;
 
 int ExternalStringTrim(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	if (entry->type == T_EOF) { returnValue->i = 0; return 3; }
@@ -4058,9 +4343,9 @@ int ExternalStringTrim(ExecutionContext *context, Value *returnValue) {
 
 int ExternalStringToByte(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	if (entry->type == T_EOF) { returnValue->i = -1; return 2; }
@@ -4070,9 +4355,9 @@ int ExternalStringToByte(ExecutionContext *context, Value *returnValue) {
 }
 
 int ExternalSystemShellExecute(ExecutionContext *context, Value *returnValue) {
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	if (entry->type != T_STR && entry->type != T_EOF) return -1;
@@ -4095,11 +4380,11 @@ int ExternalSystemShellExecute(ExecutionContext *context, Value *returnValue) {
 }
 
 int ExternalSystemShellExecuteWithWorkingDirectory(ExecutionContext *context, Value *returnValue) {
-	if (context->stackPointer < 2) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
-	uint64_t index2 = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 2) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
+	uint64_t index2 = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	if (context->heapEntriesAllocated <= index2) return -1;
 	HeapEntry *entry = &context->heap[index];
@@ -4136,9 +4421,9 @@ int ExternalSystemShellExecuteWithWorkingDirectory(ExecutionContext *context, Va
 }
 
 int ExternalSystemShellEvaluate(ExecutionContext *context, Value *returnValue) {
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	if (entry->type != T_STR && entry->type != T_EOF) return -1;
@@ -4196,9 +4481,9 @@ int ExternalSystemShellEvaluate(ExecutionContext *context, Value *returnValue) {
 
 int ExternalPrintStdErr(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	if (entry->type == T_STR) fprintf(stderr, "%.*s", (int) entry->bytes, (char *) entry->text);
@@ -4208,9 +4493,9 @@ int ExternalPrintStdErr(ExecutionContext *context, Value *returnValue) {
 
 int ExternalPrintStdErrWarning(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	static int coloredOutput = 0;
@@ -4225,9 +4510,9 @@ int ExternalPrintStdErrWarning(ExecutionContext *context, Value *returnValue) {
 
 int ExternalPrintStdErrHighlight(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	static int coloredOutput = 0;
@@ -4242,9 +4527,9 @@ int ExternalPrintStdErrHighlight(ExecutionContext *context, Value *returnValue)
 
 int ExternalPathCreateDirectory(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	returnValue->i = 0;
@@ -4267,9 +4552,9 @@ int ExternalPathCreateDirectory(ExecutionContext *context, Value *returnValue) {
 
 int ExternalPathCreateLeadingDirectories(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	returnValue->i = 0;
@@ -4300,9 +4585,9 @@ int ExternalPathCreateLeadingDirectories(ExecutionContext *context, Value *retur
 
 int ExternalPathDelete(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	returnValue->i = 0;
@@ -4319,9 +4604,9 @@ int ExternalPathDelete(ExecutionContext *context, Value *returnValue) {
 
 int ExternalPathExists(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	returnValue->i = 0;
@@ -4381,9 +4666,9 @@ bool PathDeleteRecursively(const char *path) {
 
 int ExternalPathDeleteRecursively(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	returnValue->i = 0;
@@ -4400,13 +4685,13 @@ int ExternalPathDeleteRecursively(ExecutionContext *context, Value *returnValue)
 
 int ExternalPathMove(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 2) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 2) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
-	uint64_t index2 = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	uint64_t index2 = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index2) return -1;
 	HeapEntry *entry2 = &context->heap[index2];
 	returnValue->i = 0;
@@ -4430,13 +4715,13 @@ int ExternalPathMove(ExecutionContext *context, Value *returnValue) {
 
 int ExternalFileCopy(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 2) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 2) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
-	uint64_t index2 = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	uint64_t index2 = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index2) return -1;
 	HeapEntry *entry2 = &context->heap[index2];
 	returnValue->i = 0;
@@ -4477,9 +4762,9 @@ int ExternalFileCopy(ExecutionContext *context, Value *returnValue) {
 }
 
 int ExternalSystemGetEnvironmentVariable(ExecutionContext *context, Value *returnValue) {
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	returnValue->i = 0;
@@ -4504,11 +4789,11 @@ int ExternalSystemGetEnvironmentVariable(ExecutionContext *context, Value *retur
 }
 
 int ExternalSystemSetEnvironmentVariable(ExecutionContext *context, Value *returnValue) {
-	if (context->stackPointer < 2) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
-	uint64_t index2 = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 2) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
+	uint64_t index2 = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	if (context->heapEntriesAllocated <= index2) return -1;
 	HeapEntry *entry = &context->heap[index];
@@ -4531,9 +4816,9 @@ int ExternalSystemSetEnvironmentVariable(ExecutionContext *context, Value *retur
 }
 
 int ExternalFileReadAll(ExecutionContext *context, Value *returnValue) {
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	returnValue->i = 0;
@@ -4555,11 +4840,11 @@ int ExternalFileReadAll(ExecutionContext *context, Value *returnValue) {
 }
 
 int ExternalFileWriteAll(ExecutionContext *context, Value *returnValue) {
-	if (context->stackPointer < 2) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
-	uint64_t index2 = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 2) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
+	uint64_t index2 = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	if (context->heapEntriesAllocated <= index2) return -1;
 	HeapEntry *entry = &context->heap[index];
@@ -4609,9 +4894,9 @@ int ExternalPathSetDefaultPrefixToScriptSourceDirectory(ExecutionContext *contex
 
 int ExternalPersistRead(ExecutionContext *context, Value *returnValue) {
 	(void) returnValue;
-	if (context->stackPointer < 1) return -1;
-	uint64_t index = context->stack[--context->stackPointer].i;
-	if (!context->stackIsManaged[context->stackPointer]) return -1;
+	if (context->c->stackPointer < 1) return -1;
+	uint64_t index = context->c->stack[--context->c->stackPointer].i;
+	if (!context->c->stackIsManaged[context->c->stackPointer]) return -1;
 	if (context->heapEntriesAllocated <= index) return -1;
 	HeapEntry *entry = &context->heap[index];
 	returnValue->i = 0;
@@ -4911,6 +5196,27 @@ void LineNumberLookup(ExecutionContext *context, uint32_t instructionPointer, Li
 	}
 }
 
+void PrintBackTrace(ExecutionContext *context, uint32_t instructionPointer, CoroutineState *c, const char *prefix) {
+	LineNumber lineNumber = { 0 };
+	LineNumberLookup(context, instructionPointer, &lineNumber);
+
+	if (lineNumber.importData) {
+		fprintf(stderr, "%s\t%s:%d %s %.*s\n", prefix, lineNumber.importData->path, lineNumber.lineNumber, lineNumber.function ? "in" : "",
+				lineNumber.function ? (int) lineNumber.function->textBytes : 0, lineNumber.function ? lineNumber.function->text : "");
+	}
+
+	uintptr_t btp = c->backTracePointer;
+	uintptr_t minimum = c->startedByAsync ? 1 : 0;
+
+	while (btp > minimum) {
+		BackTraceItem *link = &c->backTrace[--btp];
+		LineNumberLookup(context, link->instructionPointer - 1, &lineNumber);
+		fprintf(stderr, "%s\t%s:%d %s %.*s\n", prefix, lineNumber.importData->path ? lineNumber.importData->path : "??", 
+				lineNumber.lineNumber, lineNumber.function ? "in" : "",
+				lineNumber.function ? (int) lineNumber.function->textBytes : 0, lineNumber.function ? lineNumber.function->text : "");
+	}
+}
+
 void PrintError4(ExecutionContext *context, uint32_t instructionPointer, const char *format, ...) {
 	LineNumber lineNumber = { 0 };
 	LineNumberLookup(context, instructionPointer, &lineNumber);
@@ -4921,21 +5227,8 @@ void PrintError4(ExecutionContext *context, uint32_t instructionPointer, const c
 	vfprintf(stderr, format, arguments);
 	va_end(arguments);
 	PrintLine(lineNumber.importData, lineNumber.lineNumber);
-
 	fprintf(stderr, "Back trace:\n");
-
-	if (lineNumber.importData) {
-		fprintf(stderr, "\t%s:%d %s %.*s\n", lineNumber.importData->path, lineNumber.lineNumber, lineNumber.function ? "in" : "",
-				lineNumber.function ? (int) lineNumber.function->textBytes : 0, lineNumber.function ? lineNumber.function->text : "");
-	}
-
-	while (context->backTracePointer) {
-		BackTraceItem *link = &context->backTrace[--context->backTracePointer];
-		LineNumberLookup(context, link->instructionPointer, &lineNumber);
-		fprintf(stderr, "\t%s:%d %s %.*s\n", lineNumber.importData->path ? lineNumber.importData->path : "??", 
-				lineNumber.lineNumber, lineNumber.function ? "in" : "",
-				lineNumber.function ? (int) lineNumber.function->textBytes : 0, lineNumber.function ? lineNumber.function->text : "");
-	}
+	PrintBackTrace(context, instructionPointer, context->c, "");
 }
 
 void *FileLoad(const char *path, size_t *length) {
@@ -4988,13 +5281,18 @@ int main(int argc, char **argv) {
 	context.functionData = &builder;
 	context.mainModule = &importData;
 
-	context.stackEntriesAllocated = sizeof(context.stack) / sizeof(context.stack[0]);
 	context.heapEntriesAllocated = 2;
 	context.heap = (HeapEntry *) AllocateResize(NULL, sizeof(HeapEntry) * context.heapEntriesAllocated);
 	context.heap[0].type = T_EOF;
 	context.heap[1].type = T_ERROR;
 	context.heap[1].nextUnusedEntry = 0;
 	context.heapFirstUnusedEntry = 1;
+	context.c = (CoroutineState *) AllocateResize(0, sizeof(CoroutineState));
+	CoroutineState empty = { 0 };
+	*context.c = empty;
+	context.c->stackEntriesAllocated = sizeof(context.c->stack) / sizeof(context.c->stack[0]);
+	context.c->previousCoroutineLink = &context.allCoroutines;
+	context.allCoroutines = context.c;
 
 	int result = ScriptLoad(tokenizer, &context, &importData) ? ScriptExecute(&context, &importData) : 1;
 	ScriptFree(&context);