From 6b5cd7f5809813347b6e22e2b61cb94d5a43413b Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 7 Oct 2022 10:25:09 +0100 Subject: [PATCH] Added some node functionality to scripts --- assets/scripts/child.toy | 11 ++ assets/scripts/root.toy | 30 ++++- core/engine_node.c | 15 ++- core/engine_node.h | 1 + core/lib_engine.c | 268 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 306 insertions(+), 19 deletions(-) create mode 100644 assets/scripts/child.toy diff --git a/assets/scripts/child.toy b/assets/scripts/child.toy new file mode 100644 index 0000000..3d29a96 --- /dev/null +++ b/assets/scripts/child.toy @@ -0,0 +1,11 @@ +fn onInit(node: opaque) { + print "child.toy:onInit() called"; +} + +fn onStep(node: opaque) { + print "child.toy:onStep()"; +} + +fn onFree(node: opaque) { + print "child.toy:onFree() called"; +} diff --git a/assets/scripts/root.toy b/assets/scripts/root.toy index 00a5f7c..b9f323d 100644 --- a/assets/scripts/root.toy +++ b/assets/scripts/root.toy @@ -1,14 +1,34 @@ import standard; +import engine; -//root node can load the whole scene, and essentially act as the scene object -fn onInit() { - print "root.toy:onInit() called"; +fn makeChild(parent: opaque, fname: string, init: bool) { + var child: opaque = loadNode(fname); + + if (init) { + initNode(child); + } + + pushNode(parent, child); } -fn onStep() { +//root node can load the whole scene, and essentially act as the scene object +fn onInit(node: opaque) { + print "root.toy:onInit() called"; + + //make a child + makeChild(node, "assets/scripts/child.toy", true); //indicate whether to call "init" on the new node or not + makeChild(node, "assets/scripts/child.toy", false); + makeChild(node, "assets/scripts/child.toy", false); + + //actually, grab that first node and free it + var o: opaque = getNode(node, 0); + freeNode(o); +} + +fn onStep(node: opaque) { print clock(); } -fn onFree() { +fn onFree(node: opaque) { print "root.toy:onFree() called"; } diff --git a/core/engine_node.c b/core/engine_node.c index 5d00325..a6716ad 100644 --- a/core/engine_node.c +++ b/core/engine_node.c @@ -90,14 +90,21 @@ static void callEngineNodeLiteral(EngineNode* node, Interpreter* interpreter, Li //if this fn exists if (existsLiteralDictionary(node->functions, key)) { Literal fn = getLiteralDictionary(node->functions, key); + Literal n = TO_OPAQUE_LITERAL(node, -1); - LiteralArray dummyArray; - initLiteralArray(&dummyArray); + LiteralArray arguments; + LiteralArray returns; + initLiteralArray(&arguments); + initLiteralArray(&returns); - callLiteralFn(interpreter, fn, &dummyArray, &dummyArray); + pushLiteralArray(&arguments, n); - freeLiteralArray(&dummyArray); + callLiteralFn(interpreter, fn, &arguments, &returns); + freeLiteralArray(&arguments); + freeLiteralArray(&returns); + + freeLiteral(n); freeLiteral(fn); } diff --git a/core/engine_node.h b/core/engine_node.h index bf90745..b995c4d 100644 --- a/core/engine_node.h +++ b/core/engine_node.h @@ -14,6 +14,7 @@ typedef struct _engineNode { EngineNode* children; int capacity; int count; //includes tombstones + //TODO: add "liveCount" //toy functions, stored in a dict for flexibility LiteralDictionary* functions; diff --git a/core/lib_engine.c b/core/lib_engine.c index aa2fd35..d411f3c 100644 --- a/core/lib_engine.c +++ b/core/lib_engine.c @@ -74,6 +74,7 @@ static int nativeInitWindow(Interpreter* interpreter, LiteralArray* arguments) { return 0; } +//TODO: perhaps a returns argument would be better? static int nativeLoadRootNode(Interpreter* interpreter, LiteralArray* arguments) { if (arguments->count != 1) { interpreter->errorOutput("Incorrect number of arguments passed to loadRootNode\n"); @@ -83,6 +84,11 @@ static int nativeLoadRootNode(Interpreter* interpreter, LiteralArray* arguments) //extract the arguments Literal fname = popLiteralArray(arguments); + Literal fnameIdn = fname; + if (IS_IDENTIFIER(fname) && parseIdentifierToValue(interpreter, &fname)) { + freeLiteral(fnameIdn); + } + //check argument types if (!IS_STRING(fname)) { interpreter->errorOutput("Incorrect argument type passed to loadRootNode\n"); @@ -108,28 +114,265 @@ static int nativeLoadRootNode(Interpreter* interpreter, LiteralArray* arguments) engine.rootNode = ALLOCATE(EngineNode, 1); - //BUGFIX - unsigned char* originalTb = engine.interpreter.bytecode; - size_t originalSize = engine.interpreter.length; - int originalCount = engine.interpreter.count; - int originalCodeStart = engine.interpreter.codeStart; + //BUGFIX: make an inner-interpreter + Interpreter inner; - initEngineNode(engine.rootNode, &engine.interpreter, tb, size); + //init the inner interpreter manually + initLiteralArray(&inner.literalCache); + inner.scope = pushScope(NULL); + inner.bytecode = tb; + inner.length = size; + inner.count = 0; + inner.codeStart = -1; + inner.depth = interpreter->depth + 1; + inner.panic = false; + initLiteralArray(&inner.stack); + inner.exports = interpreter->exports; + inner.exportTypes = interpreter->exportTypes; + inner.hooks = interpreter->hooks; + setInterpreterPrint(&inner, interpreter->printOutput); + setInterpreterAssert(&inner, interpreter->assertOutput); + setInterpreterError(&inner, interpreter->errorOutput); - engine.interpreter.bytecode = originalTb; - engine.interpreter.length = originalSize; - engine.interpreter.count = originalCount; - engine.interpreter.codeStart = originalCodeStart; + initEngineNode(engine.rootNode, &inner, tb, size); //init the new node callEngineNode(engine.rootNode, &engine.interpreter, "onInit"); //cleanup + freeLiteralArray(&inner.stack); + freeLiteralArray(&inner.literalCache); freeLiteral(fname); return 0; } +static int nativeLoadNode(Interpreter* interpreter, LiteralArray* arguments) { + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments passed to loadNode\n"); + return -1; + } + + //extract the arguments + Literal fname = popLiteralArray(arguments); + + Literal fnameIdn = fname; + if (IS_IDENTIFIER(fname) && parseIdentifierToValue(interpreter, &fname)) { + freeLiteral(fnameIdn); + } + + //check argument types + if (!IS_STRING(fname)) { + interpreter->errorOutput("Incorrect argument type passed to loadNode\n"); + freeLiteral(fname); + return -1; + } + + //load the new node + size_t size = 0; + char* source = readFile(AS_STRING(fname), &size); + unsigned char* tb = compileString(source, &size); + free((void*)source); + + EngineNode* node = ALLOCATE(EngineNode, 1); + + //BUGFIX: make an inner-interpreter + Interpreter inner; + + //init the inner interpreter manually + initLiteralArray(&inner.literalCache); + inner.scope = pushScope(NULL); + inner.bytecode = tb; + inner.length = size; + inner.count = 0; + inner.codeStart = -1; + inner.depth = interpreter->depth + 1; + inner.panic = false; + initLiteralArray(&inner.stack); + inner.exports = interpreter->exports; + inner.exportTypes = interpreter->exportTypes; + inner.hooks = interpreter->hooks; + setInterpreterPrint(&inner, interpreter->printOutput); + setInterpreterAssert(&inner, interpreter->assertOutput); + setInterpreterError(&inner, interpreter->errorOutput); + + initEngineNode(node, &inner, tb, size); + + //NOTE: initNode() must be called manually + + // return the node + Literal nodeLiteral = TO_OPAQUE_LITERAL(node, -1); + pushLiteralArray(&interpreter->stack, nodeLiteral); + + //cleanup + freeLiteralArray(&inner.stack); + freeLiteralArray(&inner.literalCache); + freeLiteral(fname); + freeLiteral(nodeLiteral); + + return 1; +} + +static int nativeInitNode(Interpreter* interpreter, LiteralArray* arguments) { + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments passed to initNode\n"); + return -1; + } + + Literal node = popLiteralArray(arguments); + + Literal nodeIdn = node; //annoying + + if (!parseIdentifierToValue(interpreter, &node)) { + interpreter->errorOutput("Failed to parse node identifier to value\n"); + freeLiteral(node); + return -1; + } + + freeLiteral(nodeIdn); + + EngineNode* engineNode = AS_OPAQUE(node); + + //init the new node + callEngineNode(engineNode, &engine.interpreter, "onInit"); + + //cleanup + freeLiteral(node); + return 0; +} + +static int nativeFreeNode(Interpreter* interpreter, LiteralArray* arguments) { + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments passed to freeNode\n"); + return -1; + } + + Literal node = popLiteralArray(arguments); + + Literal nodeIdn = node; //annoying + + if (!parseIdentifierToValue(interpreter, &node)) { + interpreter->errorOutput("Failed to parse node identifier to value\n"); + freeLiteral(node); + return -1; + } + + freeLiteral(nodeIdn); + + EngineNode* engineNode = AS_OPAQUE(node); + + //free the node + callEngineNode(engineNode, &engine.interpreter, "onFree"); + + freeEngineNode(engineNode); //tombstones this node + + //cleanup + freeLiteral(node); + return 0; +} + +static int nativePushNode(Interpreter* interpreter, LiteralArray* arguments) { + //checks + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments passed to pushNode\n"); + return -1; + } + + Literal child = popLiteralArray(arguments); + Literal parent = popLiteralArray(arguments); + + Literal parentIdn = parent; //annoying + Literal childIdn = child; + + if (!parseIdentifierToValue(interpreter, &parent)) { + interpreter->errorOutput("Failed to parse parent identifier to value\n"); + freeLiteral(parent); + freeLiteral(child); + return -1; + } + + if (!parseIdentifierToValue(interpreter, &child)) { + interpreter->errorOutput("Failed to parse child identifier to value\n"); + freeLiteral(parent); + freeLiteral(child); + return -1; + } + + freeLiteral(parentIdn); + freeLiteral(childIdn); + + if (!IS_OPAQUE(parent) || !IS_OPAQUE(child)) { + interpreter->errorOutput("Incorrect argument type passed to pushNode\n"); + freeLiteral(parent); + freeLiteral(child); + return -1; + } + + //push the node + EngineNode* parentNode = AS_OPAQUE(parent); + EngineNode* childNode = AS_OPAQUE(child); + + pushEngineNode(parentNode, childNode); + + //no return value + freeLiteral(parent); + freeLiteral(child); + + return 0; +} + +static int nativeGetNode(Interpreter* interpreter, LiteralArray* arguments) { + //checks + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments passed to getNode\n"); + return -1; + } + + Literal index = popLiteralArray(arguments); + Literal parent = popLiteralArray(arguments); + + Literal parentIdn = parent; //annoying + + if (!parseIdentifierToValue(interpreter, &parent)) { + interpreter->errorOutput("Failed to parse parent identifier to value\n"); + freeLiteral(parent); + freeLiteral(index); + return -1; + } + + freeLiteral(parentIdn); + + if (!IS_OPAQUE(parent) || !IS_INTEGER(index)) { + interpreter->errorOutput("Incorrect argument type passed to getNode\n"); + freeLiteral(parent); + freeLiteral(index); + return -1; + } + + //push the node + EngineNode* parentNode = AS_OPAQUE(parent); + int intIndex = AS_INTEGER(index); + + if (intIndex < 0 || intIndex >= parentNode->count) { + interpreter->errorOutput("index out of bounds in getNode\n"); + freeLiteral(parent); + freeLiteral(index); + return -1; + } + + EngineNode* childNode = &parentNode->children[intIndex]; + Literal child = TO_OPAQUE_LITERAL(childNode, -1); + + pushLiteralArray(&interpreter->stack, child); + + //no return value + freeLiteral(parent); + freeLiteral(child); + freeLiteral(index); + + return 1; +} + //call the hook typedef struct Natives { char* name; @@ -141,6 +384,11 @@ int hookEngine(Interpreter* interpreter, Literal identifier, Literal alias) { Natives natives[] = { {"initWindow", nativeInitWindow}, {"loadRootNode", nativeLoadRootNode}, + {"loadNode", nativeLoadNode}, + {"initNode", nativeInitNode}, + {"freeNode", nativeFreeNode}, + {"pushNode", nativePushNode}, + {"getNode", nativeGetNode}, {NULL, NULL} };