From 73a6457025ef41bf7ebdf2f0f33d7d2645e57e0e Mon Sep 17 00:00:00 2001 From: nakst <> Date: Sun, 6 Feb 2022 12:18:40 +0000 Subject: [PATCH] add ExternalPassREPLResult --- apps/script_console.cpp | 177 ++++++++++++++++++++++++++++++++++--- util/script.c | 188 +++++++++++++++++++++++++++++++++++----- 2 files changed, 330 insertions(+), 35 deletions(-) diff --git a/apps/script_console.cpp b/apps/script_console.cpp index 0927f6b..a214010 100644 --- a/apps/script_console.cpp +++ b/apps/script_console.cpp @@ -1,6 +1,8 @@ #define ES_INSTANCE_TYPE Instance #include +// TODO Resizing the window after calling DirectoryEnumerateChildrenRecursively() is slow. + struct Instance : EsInstance { EsThreadInformation scriptThread; char *inputText; @@ -15,6 +17,8 @@ struct Instance : EsInstance { char *outputLineBuffer; size_t outputLineBufferBytes; size_t outputLineBufferAllocated; + bool anyOutput; + bool gotREPLResult; }; void AddPrompt(Instance *instance); @@ -166,6 +170,14 @@ int ExternalPrintStdErrHighlight(ExecutionContext *context, Value *returnValue) return ExternalPrintStdErr(context, returnValue); } +int ExternalSystemSleepMs(ExecutionContext *context, Value *returnValue) { + (void) returnValue; + if (context->c->stackPointer < 1) return -1; + int64_t ms = context->c->stack[--context->c->stackPointer].i; + if (ms > 0) EsSleep(ms); + return 1; +} + int ExternalSystemGetHostName(ExecutionContext *context, Value *returnValue) { (void) context; RETURN_STRING_COPY("Essence", 7); @@ -335,6 +347,10 @@ EXTERNAL_STUB(ExternalSystemSetEnvironmentVariable); // --------------------------------- User interface. #define COLOR_BACKGROUND (0xFFFDFDFD) +#define COLOR_ROW_ODD (0xFFF4F4F4) +#define COLOR_OUTPUT_DECORATION_IN_PROGRESS (0xFFFF7F00) +#define COLOR_OUTPUT_DECORATION_SUCCESS (0xFF3070FF) +#define COLOR_OUTPUT_DECORATION_FAILURE (0xFFFF3040) #define COLOR_TEXT_MAIN (0xFF010102) #define COLOR_TEXT_LIGHT (0xFF606062) #define TEXT_SIZE_DEFAULT (14) @@ -386,6 +402,31 @@ const EsStyle styleOutputParagraph = { }, }; +const EsStyle styleOutputParagraphStrong = { + .metrics = { + .mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_FONT_WEIGHT + | ES_THEME_METRICS_TEXT_SIZE | ES_THEME_METRICS_TEXT_ALIGN | ES_THEME_METRICS_TEXT_COLOR, + .textColor = COLOR_TEXT_MAIN, + .textAlign = ES_TEXT_H_LEFT | ES_TEXT_WRAP | ES_TEXT_V_TOP, + .textSize = TEXT_SIZE_OUTPUT, + .fontFamily = ES_FONT_SANS, + .fontWeight = 6, + }, +}; + +const EsStyle styleOutputParagraphItalic = { + .metrics = { + .mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_FONT_WEIGHT | ES_THEME_METRICS_IS_ITALIC + | ES_THEME_METRICS_TEXT_SIZE | ES_THEME_METRICS_TEXT_ALIGN | ES_THEME_METRICS_TEXT_COLOR, + .textColor = COLOR_TEXT_MAIN, + .textAlign = ES_TEXT_H_LEFT | ES_TEXT_WRAP | ES_TEXT_V_TOP, + .textSize = TEXT_SIZE_OUTPUT, + .fontFamily = ES_FONT_SANS, + .fontWeight = 4, + .isItalic = true, + }, +}; + const EsStyle stylePromptText = { .metrics = { .mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_FONT_WEIGHT @@ -454,7 +495,7 @@ const EsStyle styleOutputDecorationInProgress = { .appearance = { .enabled = true, - .backgroundColor = 0xFFFF7F00, + .backgroundColor = COLOR_OUTPUT_DECORATION_IN_PROGRESS, }, }; @@ -466,7 +507,7 @@ const EsStyle styleOutputDecorationSuccess = { .appearance = { .enabled = true, - .backgroundColor = 0xFF3070FF, + .backgroundColor = COLOR_OUTPUT_DECORATION_SUCCESS, }, }; @@ -478,14 +519,120 @@ const EsStyle styleOutputDecorationFailure = { .appearance = { .enabled = true, - .backgroundColor = 0xFFFF3040, + .backgroundColor = COLOR_OUTPUT_DECORATION_FAILURE, }, }; +const EsStyle styleListRowEven = { + .metrics = { + .mask = ES_THEME_METRICS_INSETS, + .insets = ES_RECT_1(3), + }, + + .appearance = { + .enabled = true, + .backgroundColor = COLOR_BACKGROUND, + }, +}; + +const EsStyle styleListRowOdd = { + .metrics = { + .mask = ES_THEME_METRICS_INSETS, + .insets = ES_RECT_1(3), + }, + + .appearance = { + .enabled = true, + .backgroundColor = COLOR_ROW_ODD, + }, +}; + +void AddREPLResult(ExecutionContext *context, EsElement *parent, Node *type, Value value) { + // TODO Truncating/scrolling/collapsing/saving/copying output. + // TODO Letting scripts register custom views for structs. + + size_t bytes; + + if (type->type == T_INT) { + char *buffer = EsStringAllocateAndFormat(&bytes, "(int) %d", value.i); + EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes); + } else if (type->type == T_BOOL) { + char *buffer = EsStringAllocateAndFormat(&bytes, "%z", value.i ? "true" : "false"); + EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes); + } else if (type->type == T_FLOAT) { + char *buffer = EsStringAllocateAndFormat(&bytes, "(float) %F", value.f); + EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes); + } else if (type->type == T_STR) { + EsAssert(context->heapEntriesAllocated > (uint64_t) value.i); + HeapEntry *entry = &context->heap[value.i]; + const char *valueText; + size_t valueBytes; + ScriptHeapEntryToString(context, entry, &valueText, &valueBytes); + char *buffer = EsStringAllocateAndFormat(&bytes, "\"%s\"", valueBytes, valueText); + EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes); + } else if (type->type == T_LIST) { + EsPanel *panel = EsPanelCreate(parent, ES_CELL_H_FILL | ES_PANEL_VERTICAL | ES_PANEL_STACK); + EsAssert(context->heapEntriesAllocated > (uint64_t) value.i); + HeapEntry *entry = &context->heap[value.i]; + EsAssert(entry->type == T_LIST); + + if (!entry->length) { + EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphItalic, + EsLiteral("Empty list.\n")); + } + + for (uintptr_t i = 0; i < entry->length; i++) { + EsPanel *item = EsPanelCreate(panel, ES_CELL_H_FILL, (i % 2) ? &styleListRowOdd : &styleListRowEven); + AddREPLResult(context, item, type->firstChild, entry->list[i]); + } + } else if (type->type == T_FUNCPTR) { + EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphItalic, + EsLiteral("Function pointer.\n")); + } else if (type->type == T_STRUCT) { + EsPanel *panel = EsPanelCreate(parent, ES_CELL_H_FILL | ES_PANEL_VERTICAL | ES_PANEL_STACK); + char *buffer = EsStringAllocateAndFormat(&bytes, "%s:", type->token.textBytes, type->token.text); + EsTextDisplayCreate(panel, ES_CELL_H_FILL, &styleOutputParagraph, buffer, bytes); + EsAssert(context->heapEntriesAllocated > (uint64_t) value.i); + HeapEntry *entry = &context->heap[value.i]; + EsAssert(entry->type == T_STRUCT); + uintptr_t i = 0; + + Node *field = type->firstChild; + + while (field) { + EsAssert(i != entry->fieldCount); + EsPanel *item = EsPanelCreate(panel, ES_CELL_H_FILL, (i % 2) ? &styleListRowEven : &styleListRowOdd); + AddREPLResult(context, item, field->firstChild, entry->fields[i]); + field = field->sibling; + i++; + } + } else if (type->type == T_NULL) { + char *buffer = EsStringAllocateAndFormat(&bytes, "%z", "null"); + EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes); + } else { + EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphItalic, + EsLiteral("The type of the result was not recognized.\n")); + } +} + +void ExternalPassREPLResult(ExecutionContext *context, Value value) { + Instance *instance = scriptInstance; + + if (instance->gotREPLResult) return; + if (instance->outputLineBufferBytes) AddOutput(instance, EsLiteral("\n")); + + instance->anyOutput = true; + instance->gotREPLResult = true; + + EsMessageMutexAcquire(); + AddREPLResult(context, instance->outputPanel, context->functionData->replResultType, value); + EsMessageMutexRelease(); +} + void ScriptThread(EsGeneric _instance) { Instance *instance = (Instance *) _instance.p; scriptInstance = instance; - int result = ScriptExecuteFromFile(EsLiteral("in"), instance->inputText, instance->inputBytes); + int result = ScriptExecuteFromFile(EsLiteral("in"), instance->inputText, instance->inputBytes, true); instance->inputText = nullptr; if (instance->outputLineBufferBytes) { @@ -494,6 +641,14 @@ void ScriptThread(EsGeneric _instance) { EsMessageMutexAcquire(); + if (!instance->anyOutput) { + EsElementDestroy(EsElementGetLayoutParent(instance->outputPanel)); + } else { + instance->anyOutput = false; + } + + instance->gotREPLResult = false; + if (result == 0) { EsSpacerChangeStyle(instance->outputDecoration, &styleOutputDecorationSuccess); } else { @@ -525,16 +680,8 @@ void EnterCommand(Instance *instance) { instance->outputDecoration = EsSpacerCreate(outputPanelWrapper, ES_CELL_V_FILL, &styleOutputDecorationInProgress); instance->outputPanel = EsPanelCreate(outputPanelWrapper, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_VERTICAL, &styleOutputPanel); - const char *inputPrefix = "void Start() {\n"; - const char *inputSuffix = "\n}"; - char *script = (char *) EsHeapAllocate(dataBytes + EsCStringLength(inputPrefix) + EsCStringLength(inputSuffix), false); - EsMemoryCopy(script, inputPrefix, EsCStringLength(inputPrefix)); - EsMemoryCopy(script + EsCStringLength(inputPrefix), data, dataBytes); - EsMemoryCopy(script + EsCStringLength(inputPrefix) + dataBytes, inputSuffix, EsCStringLength(inputSuffix)); - EsHeapFree(data); - - instance->inputText = script; - instance->inputBytes = dataBytes + EsCStringLength(inputPrefix) + EsCStringLength(inputSuffix); + instance->inputText = data; + instance->inputBytes = dataBytes; EsAssert(ES_SUCCESS == EsThreadCreate(ScriptThread, &instance->scriptThread, instance)); EsHandleClose(instance->scriptThread.handle); } @@ -551,6 +698,8 @@ int InputTextboxMessage(EsElement *element, EsMessage *message) { } void AddOutput(Instance *instance, const char *text, size_t textBytes) { + instance->anyOutput = true; + for (uintptr_t i = 0; i < textBytes; i++) { if (text[i] == '\n') { EsMessageMutexAcquire(); diff --git a/util/script.c b/util/script.c index 0072fc6..18848e6 100644 --- a/util/script.c +++ b/util/script.c @@ -85,6 +85,7 @@ #define T_LIST (92) #define T_IMPORT_PATH (93) #define T_LIST_LITERAL (94) +#define T_REPL_RESULT (95) #define T_EXIT_SCOPE (100) #define T_END_FUNCTION (101) @@ -250,6 +251,7 @@ typedef struct FunctionBuilder { bool isPersistentVariable, isDotAssignment, isListAssignment; uintptr_t globalVariableOffset; struct ImportData *importData; // Only valid during script loading. + Node *replResultType; } FunctionBuilder; typedef struct BackTraceItem { @@ -381,10 +383,10 @@ ImportData *importedModules; ImportData **importedModulesLink = &importedModules; // Forward declarations: -Node *ParseBlock(Tokenizer *tokenizer); +Node *ParseBlock(Tokenizer *tokenizer, bool replMode); Node *ParseExpression(Tokenizer *tokenizer, bool allowAssignment, uint8_t precedence); void ScriptPrintNode(Node *node, int indent); -bool ScriptLoad(Tokenizer tokenizer, ExecutionContext *context, ImportData *importData); +bool ScriptLoad(Tokenizer tokenizer, ExecutionContext *context, ImportData *importData, bool replMode); void ScriptFreeCoroutine(CoroutineState *c); uintptr_t HeapAllocate(ExecutionContext *context); @@ -409,6 +411,7 @@ void PrintError4(ExecutionContext *context, uint32_t instructionPointer, const c void PrintBackTrace(ExecutionContext *context, uint32_t instructionPointer, CoroutineState *c, const char *prefix); void *FileLoad(const char *path, size_t *length); CoroutineState *ExternalCoroutineWaitAny(ExecutionContext *context); +void ExternalPassREPLResult(ExecutionContext *context, Value value); // --------------------------------- Base module. @@ -469,6 +472,7 @@ char baseModuleSource[] = { "int SystemGetProcessorCount() #extcall;" "bool SystemRunningAsAdministrator() #extcall;" "str SystemGetHostName() #extcall;" + "void SystemSleepMs(int ms) #extcall;" "int RandomInt(int min, int max) #extcall;" "str UUIDGenerate() {" @@ -682,6 +686,7 @@ int ExternalSystemGetEnvironmentVariable(ExecutionContext *context, Value *retur int ExternalSystemSetEnvironmentVariable(ExecutionContext *context, Value *returnValue); int ExternalSystemRunningAsAdministrator(ExecutionContext *context, Value *returnValue); int ExternalSystemGetHostName(ExecutionContext *context, Value *returnValue); +int ExternalSystemSleepMs(ExecutionContext *context, Value *returnValue); int ExternalPathCreateDirectory(ExecutionContext *context, Value *returnValue); int ExternalPathDelete(ExecutionContext *context, Value *returnValue); int ExternalPathExists(ExecutionContext *context, Value *returnValue); @@ -718,6 +723,7 @@ ExternalFunction externalFunctions[] = { { .cName = "SystemSetEnvironmentVariable", .callback = ExternalSystemSetEnvironmentVariable }, { .cName = "SystemRunningAsAdministrator", .callback = ExternalSystemRunningAsAdministrator }, { .cName = "SystemGetHostName", .callback = ExternalSystemGetHostName }, + { .cName = "SystemSleepMs", .callback = ExternalSystemSleepMs }, { .cName = "PathExists", .callback = ExternalPathExists }, { .cName = "PathIsFile", .callback = ExternalPathIsFile }, { .cName = "PathIsDirectory", .callback = ExternalPathIsDirectory }, @@ -976,7 +982,10 @@ Node *ParseType(Tokenizer *tokenizer, bool maybe, bool allowVoid) { node->type = node->token.type; if (!allowVoid && node->type == T_VOID) { - PrintError2(tokenizer, node, "The 'void' type is not allowed here.\n"); + if (!maybe) { + PrintError2(tokenizer, node, "The 'void' type is not allowed here.\n"); + } + return NULL; } @@ -1049,6 +1058,8 @@ Node *ParseCall(Tokenizer *tokenizer, Node *function) { if (token.type == T_RIGHT_ROUND) { TokenNext(tokenizer); break; + } else if (token.type == T_ERROR) { + return NULL; } if (arguments->firstChild) { @@ -1291,7 +1302,7 @@ Node *ParseIf(Tokenizer *tokenizer) { if (token.type == T_LEFT_FANCY) { TokenNext(tokenizer); - node->firstChild->sibling = ParseBlock(tokenizer); + node->firstChild->sibling = ParseBlock(tokenizer, false); if (!node->firstChild->sibling) return NULL; } else { Node *wrapper = (Node *) AllocateFixed(sizeof(Node)); @@ -1319,7 +1330,7 @@ Node *ParseIf(Tokenizer *tokenizer) { if (!node->firstChild->sibling->sibling) return NULL; } else if (token.type == T_LEFT_FANCY) { TokenNext(tokenizer); - node->firstChild->sibling->sibling = ParseBlock(tokenizer); + node->firstChild->sibling->sibling = ParseBlock(tokenizer, false); if (!node->firstChild->sibling->sibling) return NULL; } else { node->firstChild->sibling->sibling = ParseExpression(tokenizer, true, 0); @@ -1337,7 +1348,7 @@ Node *ParseIf(Tokenizer *tokenizer) { return node; } -Node *ParseVariableDeclarationOrExpression(Tokenizer *tokenizer) { +Node *ParseVariableDeclarationOrExpression(Tokenizer *tokenizer, bool replMode) { Tokenizer copy = *tokenizer; bool isVariableDeclaration = false; @@ -1356,6 +1367,12 @@ Node *ParseVariableDeclarationOrExpression(Tokenizer *tokenizer) { *tokenizer = copy; if (isVariableDeclaration) { + // TODO Variable support in replMode. + if (replMode) { + PrintError(tokenizer, "Variables are not yet supported in the console.\n"); + return NULL; + } + Node *declaration = (Node *) AllocateFixed(sizeof(Node)); declaration->type = T_DECLARE; declaration->firstChild = ParseType(tokenizer, false, false); @@ -1381,6 +1398,19 @@ Node *ParseVariableDeclarationOrExpression(Tokenizer *tokenizer) { Node *expression = ParseExpression(tokenizer, true, 0); if (!expression) return NULL; + if (replMode) { + Token end = TokenPeek(tokenizer); + + if (end.type == T_ERROR) { + return NULL; + } else if (end.type == T_EOF) { + Node *replResult = (Node *) AllocateFixed(sizeof(Node)); + replResult->type = T_REPL_RESULT; + replResult->firstChild = expression; + return replResult; + } + } + Token semicolon = TokenNext(tokenizer); if (semicolon.type != T_SEMICOLON) { @@ -1392,7 +1422,7 @@ Node *ParseVariableDeclarationOrExpression(Tokenizer *tokenizer) { } } -Node *ParseBlock(Tokenizer *tokenizer) { +Node *ParseBlock(Tokenizer *tokenizer, bool replMode) { Node *node = (Node *) AllocateFixed(sizeof(Node)); Node **link = &node->firstChild; node->type = T_BLOCK; @@ -1404,7 +1434,7 @@ Node *ParseBlock(Tokenizer *tokenizer) { if (token.type == T_ERROR) { return NULL; - } else if (token.type == T_RIGHT_FANCY) { + } else if (token.type == (replMode ? T_EOF : T_RIGHT_FANCY)) { TokenNext(tokenizer); return node; } else if (token.type == T_IF) { @@ -1426,7 +1456,7 @@ Node *ParseBlock(Tokenizer *tokenizer) { } if (token.type == T_LEFT_FANCY) { - node->firstChild->sibling = ParseBlock(tokenizer); + node->firstChild->sibling = ParseBlock(tokenizer, false); if (!node->firstChild->sibling) return NULL; } else { node->firstChild->sibling = (Node *) AllocateFixed(sizeof(Node)); @@ -1441,7 +1471,7 @@ Node *ParseBlock(Tokenizer *tokenizer) { Node *node = (Node *) AllocateFixed(sizeof(Node)); node->type = T_FOR; node->token = TokenNext(tokenizer); - node->firstChild = ParseVariableDeclarationOrExpression(tokenizer); + node->firstChild = ParseVariableDeclarationOrExpression(tokenizer, false); if (!node->firstChild) return NULL; node->firstChild->sibling = ParseExpression(tokenizer, false, 0); if (!node->firstChild->sibling) return NULL; @@ -1464,7 +1494,7 @@ Node *ParseBlock(Tokenizer *tokenizer) { } if (token.type == T_LEFT_FANCY) { - node->firstChild->sibling->sibling->sibling = ParseBlock(tokenizer); + node->firstChild->sibling->sibling->sibling = ParseBlock(tokenizer, false); if (!node->firstChild->sibling->sibling->sibling) return NULL; } else { node->firstChild->sibling->sibling->sibling = (Node *) AllocateFixed(sizeof(Node)); @@ -1480,6 +1510,12 @@ Node *ParseBlock(Tokenizer *tokenizer) { *link = wrapper; link = &wrapper->sibling; } else if (token.type == T_RETURN || token.type == T_ASSERT) { + if (replMode) { + PrintError2(tokenizer, node, "%s statements cannot be used in the console.\n", + token.type == T_RETURN ? "Return" : "Assert"); + return NULL; + } + Node *node = (Node *) AllocateFixed(sizeof(Node)); node->type = token.type; node->token = TokenNext(tokenizer); @@ -1499,12 +1535,12 @@ Node *ParseBlock(Tokenizer *tokenizer) { } } else if (token.type == T_LEFT_FANCY) { TokenNext(tokenizer); - Node *block = ParseBlock(tokenizer); + Node *block = ParseBlock(tokenizer, false); if (!block) return NULL; *link = block; link = &block->sibling; } else { - Node *node = ParseVariableDeclarationOrExpression(tokenizer); + Node *node = ParseVariableDeclarationOrExpression(tokenizer, replMode); if (!node) return NULL; *link = node; link = &node->sibling; @@ -1595,7 +1631,7 @@ Node *ParseGlobalVariableOrFunctionDefinition(Tokenizer *tokenizer, bool allowGl Node *body = (Node *) AllocateFixed(sizeof(Node)); body->type = T_FUNCBODY; functionPointerType->sibling = body; - body->firstChild = ParseBlock(tokenizer); + body->firstChild = ParseBlock(tokenizer, false); if (!body->firstChild) { return NULL; @@ -1643,6 +1679,51 @@ Node *ParseGlobalVariableOrFunctionDefinition(Tokenizer *tokenizer, bool allowGl return node; } +Node *ParseRootREPL(Tokenizer *tokenizer) { + Node *root = (Node *) AllocateFixed(sizeof(Node)); + root->type = T_ROOT; + Node **link = &root->firstChild; + + Node *node = (Node *) AllocateFixed(sizeof(Node)); + node->type = T_IMPORT; + node->token.type = T_INLINE; + node->token.text = "#inline"; + node->token.textBytes = 7; + node->firstChild = (Node *) AllocateFixed(sizeof(Node)); + node->firstChild->type = T_IMPORT_PATH; + node->firstChild->token.type = T_STRING_LITERAL; + node->firstChild->token.text = "__base_module__"; + node->firstChild->token.textBytes = 15; + *link = node; + link = &node->sibling; + + Node *function = (Node *) AllocateFixed(sizeof(Node)); + *link = function; + link = &function->sibling; + function->type = T_FUNCTION; + function->token.text = startFunction; + function->token.textBytes = startFunctionBytes; + Node *functionPointerType = (Node *) AllocateFixed(sizeof(Node)); + function->firstChild = functionPointerType; + functionPointerType->type = T_FUNCPTR; + Node *functionArguments = (Node *) AllocateFixed(sizeof(Node)); + functionPointerType->firstChild = functionArguments; + functionArguments->type = T_ARGUMENTS; + Node *functionReturnType = (Node *) AllocateFixed(sizeof(Node)); + functionArguments->sibling = functionReturnType; + functionReturnType->type = T_VOID; + Node *functionBody = (Node *) AllocateFixed(sizeof(Node)); + functionPointerType->sibling = functionBody; + functionBody->type = T_FUNCBODY; + functionBody->firstChild = ParseBlock(tokenizer, true); + + if (!functionBody->firstChild) { + return NULL; + } + + return root; +} + Node *ParseRoot(Tokenizer *tokenizer) { Node *root = (Node *) AllocateFixed(sizeof(Node)); root->type = T_ROOT; @@ -2037,7 +2118,7 @@ bool ASTSetScopes(Tokenizer *tokenizer, ExecutionContext *context, Node *node, S t.input = (const char *) fileData; t.line = 1; - if (!ScriptLoad(t, context, node->importData)) { + if (!ScriptLoad(t, context, node->importData, false)) { return false; } } @@ -2121,6 +2202,19 @@ bool ASTLookupTypeIdentifiers(Tokenizer *tokenizer, Node *node) { child = child->sibling; } + if (node->type == T_FUNCPTR) { + Node *type = node->firstChild->sibling; + + if (type->type == T_IDENTIFIER) { + Node *lookup = ScopeLookup(tokenizer, type, false); + if (!lookup) return false; + Node *copy = (Node *) AllocateFixed(sizeof(Node)); + *copy = *lookup; + copy->sibling = NULL; + node->firstChild->sibling = copy; + } + } + if (node->type == T_DECLARE || node->type == T_ARGUMENT || node->type == T_NEW || node->type == T_LIST) { Node *type = node->firstChild; @@ -2169,7 +2263,8 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) { || node->type == T_BOOL || node->type == T_VOID || node->type == T_IDENTIFIER || node->type == T_ARGUMENTS || node->type == T_ARGUMENT || node->type == T_STRUCT || node->type == T_FUNCTYPE || node->type == T_IMPORT || node->type == T_IMPORT_PATH - || node->type == T_FUNCPTR || node->type == T_FUNCBODY || node->type == T_FUNCTION) { + || node->type == T_FUNCPTR || node->type == T_FUNCBODY || node->type == T_FUNCTION + || node->type == T_REPL_RESULT) { } else if (node->type == T_NUMERIC_LITERAL) { size_t dotCount = 0; @@ -3148,6 +3243,17 @@ bool FunctionBuilderRecurse(Tokenizer *tokenizer, Node *node, FunctionBuilder *b Value v; v.i = node->type == T_TRUE ? 1 : 0; FunctionBuilderAppend(builder, &v, sizeof(v)); + } else if (node->type == T_REPL_RESULT) { + if (!ASTMatching(node->firstChild->expressionType, &globalExpressionTypeVoid)) { + FunctionBuilderAppend(builder, &node->type, sizeof(node->type)); + } + + if (builder->replResultType) { + PrintError2(tokenizer, node, "Multiple REPL results are not allowed.\n"); + return false; + } + + builder->replResultType = node->firstChild->expressionType; } else { PrintDebug("FunctionBuilderRecurse %d\n", node->type); Assert(false); @@ -4442,6 +4548,9 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex instructionPointer = 1; // There is a T_AWAIT command at address 1. goto callCommand; } + } else if (command == T_REPL_RESULT) { + if (context->c->stackPointer < 1) return -1; + ExternalPassREPLResult(context, context->c->stack[--context->c->stackPointer]); } else if (command == T_END_FUNCTION || command == T_EXTCALL) { if (command == T_EXTCALL) { uint16_t index = functionData[instructionPointer + 0] + (functionData[instructionPointer + 1] << 8); @@ -4620,11 +4729,11 @@ bool ScriptParseOptions(ExecutionContext *context) { return true; } -bool ScriptLoad(Tokenizer tokenizer, ExecutionContext *context, ImportData *importData) { +bool ScriptLoad(Tokenizer tokenizer, ExecutionContext *context, ImportData *importData, bool replMode) { Node *previousRootNode = context->rootNode; ImportData *previousImportData = context->functionData->importData; - context->rootNode = ParseRoot(&tokenizer); + context->rootNode = replMode ? ParseRootREPL(&tokenizer) : ParseRoot(&tokenizer); context->functionData->importData = tokenizer.module; uint8_t b = 0; // Make sure no function can start at 0. @@ -4834,7 +4943,7 @@ void PrintLine(ImportData *importData, uintptr_t line) { PrintDebug(">> %.*s\n", (int) length, &((char *) importData->fileData)[position]); } -int ScriptExecuteFromFile(char *scriptPath, size_t scriptPathBytes, char *fileData, size_t fileDataBytes) { +int ScriptExecuteFromFile(char *scriptPath, size_t scriptPathBytes, char *fileData, size_t fileDataBytes, bool replMode) { Tokenizer tokenizer = { 0 }; ImportData importData = { 0 }; importData.path = scriptPath; @@ -4864,7 +4973,7 @@ int ScriptExecuteFromFile(char *scriptPath, size_t scriptPathBytes, char *fileDa context.c->previousCoroutineLink = &context.allCoroutines; context.allCoroutines = context.c; - int result = ScriptLoad(tokenizer, &context, &importData) ? ScriptExecute(&context, &importData) : 1; + int result = ScriptLoad(tokenizer, &context, &importData, replMode) ? ScriptExecute(&context, &importData) : 1; ScriptFree(&context); importedModules = NULL; @@ -5606,6 +5715,37 @@ int ExternalRandomInt(ExecutionContext *context, Value *returnValue) { return 2; } +void *SystemSleepThread(void *_coroutine) { + CoroutineState *coroutine = (CoroutineState *) _coroutine; + struct timespec sleepTime; + uint64_t x = 1000000 * coroutine->externalCoroutineData.i; + sleepTime.tv_sec = x / 1000000000; + sleepTime.tv_nsec = x % 1000000000; + nanosleep(&sleepTime, NULL); + ExternalCoroutineDone(coroutine); + return NULL; +} + +int ExternalSystemSleepMs(ExecutionContext *context, Value *returnValue) { + (void) returnValue; + if (context->c->externalCoroutine) return 1; + if (context->c->stackPointer < 1) return -1; + context->c->externalCoroutineData = context->c->stack[--context->c->stackPointer]; +#ifdef __linux__ + pthread_t thread; + pthread_create(&thread, NULL, SystemSleepThread, context->c); + pthread_detach(thread); + return 4; +#else + struct timespec sleepTime; + uint64_t x = 1000000 * coroutine->externalCoroutineData.i; + sleepTime.tv_sec = x / 1000000000; + sleepTime.tv_nsec = x % 1000000000; + nanosleep(&sleepTime, NULL); + return 1; +#endif +} + CoroutineState *ExternalCoroutineWaitAny(ExecutionContext *context) { (void) context; while (sem_wait(&externalCoroutineSemaphore) == -1); @@ -5618,6 +5758,12 @@ CoroutineState *ExternalCoroutineWaitAny(ExecutionContext *context) { return unblocked; } +void ExternalPassREPLResult(ExecutionContext *context, Value value) { + (void) context; + (void) value; + Assert(false); +} + void *AllocateFixed(size_t bytes) { if (!bytes) { return NULL; @@ -5785,7 +5931,7 @@ int main(int argc, char **argv) { return 1; } - int result = ScriptExecuteFromFile(scriptPath, strlen(scriptPath), data, dataBytes); + int result = ScriptExecuteFromFile(scriptPath, strlen(scriptPath), data, dataBytes, false); while (fixedAllocationBlocks) { void *block = fixedAllocationBlocks;