mirror of https://gitlab.com/nakst/essence
add ExternalPassREPLResult
This commit is contained in:
parent
d3653d2958
commit
73a6457025
|
@ -1,6 +1,8 @@
|
|||
#define ES_INSTANCE_TYPE Instance
|
||||
#include <essence.h>
|
||||
|
||||
// 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();
|
||||
|
|
188
util/script.c
188
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;
|
||||
|
|
Loading…
Reference in New Issue