diff --git a/Toy b/Toy index aeda0a0..ee226ea 160000 --- a/Toy +++ b/Toy @@ -1 +1 @@ -Subproject commit aeda0a0d949768c342b3bb7e275f2afdc27c643e +Subproject commit ee226ea426b31d08dddb22d73e25f0f9e76e0488 diff --git a/box/box_engine.c b/box/box_engine.c index 80838b1..ab8f7ff 100644 --- a/box/box_engine.c +++ b/box/box_engine.c @@ -58,8 +58,8 @@ void Box_initEngine() { Toy_injectNativeHook(&engine.interpreter, "runner", Toy_hookRunner); size_t size = 0; - char* source = Toy_readFile("./assets/scripts/init.toy", &size); - unsigned char* tb = Toy_compileString(source, &size); + const char* source = Toy_readFile("./assets/scripts/init.toy", &size); + const unsigned char* tb = Toy_compileString(source, &size); free((void*)source); Toy_runInterpreter(&engine.interpreter, tb, size); diff --git a/box/box_engine_node.c b/box/box_engine_node.c index dd9c899..b9f5c3b 100644 --- a/box/box_engine_node.c +++ b/box/box_engine_node.c @@ -3,7 +3,7 @@ #include "toy_memory.h" -void Box_initEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, void* tb, size_t size) { +void Box_initEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, const unsigned char* tb, size_t size) { //init // node->freeMemory = freeMemory; node->functions = TOY_ALLOCATE(Toy_LiteralDictionary, 1); @@ -129,7 +129,7 @@ Toy_Literal Box_callEngineNodeLiteral(Box_EngineNode* node, Toy_Interpreter* int return ret; } -Toy_Literal Box_callEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, char* fnName, Toy_LiteralArray* args) { +Toy_Literal Box_callEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, const char* fnName, Toy_LiteralArray* args) { //call "fnName" on this node, and all children, if it exists Toy_Literal key = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString(fnName)); @@ -177,7 +177,7 @@ void Box_callRecursiveEngineNodeLiteral(Box_EngineNode* node, Toy_Interpreter* i } } -void Box_callRecursiveEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, char* fnName, Toy_LiteralArray* args) { +void Box_callRecursiveEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, const char* fnName, Toy_LiteralArray* args) { //call "fnName" on this node, and all children, if it exists Toy_Literal key = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString(fnName)); @@ -186,7 +186,7 @@ void Box_callRecursiveEngineNode(Box_EngineNode* node, Toy_Interpreter* interpre Toy_freeLiteral(key); } -int Box_loadTextureEngineNode(Box_EngineNode* node, char* fname) { +int Box_loadTextureEngineNode(Box_EngineNode* node, const char* fname) { SDL_Surface* surface = IMG_Load(fname); if (surface == NULL) { diff --git a/box/box_engine_node.h b/box/box_engine_node.h index c4b029d..dabb6a9 100644 --- a/box/box_engine_node.h +++ b/box/box_engine_node.h @@ -24,7 +24,7 @@ typedef struct Box_private_engineNode { //my opaque type tag int tag; - int _unused0; + // int _unused0; //use Toy's memory model Box_EngineNode** children; @@ -37,17 +37,17 @@ typedef struct Box_private_engineNode { //TODO: depth } Box_EngineNode; -BOX_API void Box_initEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, void* tb, size_t size); //run bytecode, then grab all top-level function literals +BOX_API void Box_initEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, const unsigned char* tb, size_t size); //run bytecode, then grab all top-level function literals BOX_API void Box_pushEngineNode(Box_EngineNode* node, Box_EngineNode* child); //push to the array (prune tombstones when expanding/copying) BOX_API void Box_freeEngineNode(Box_EngineNode* node); //free and tombstone this node BOX_API Toy_Literal Box_callEngineNodeLiteral(Box_EngineNode* node, Toy_Interpreter* interpreter, Toy_Literal key, Toy_LiteralArray* args); -BOX_API Toy_Literal Box_callEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, char* fnName, Toy_LiteralArray* args); //call "fnName" on this node, and only this node, if it exists +BOX_API Toy_Literal Box_callEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, const char* fnName, Toy_LiteralArray* args); //call "fnName" on this node, and only this node, if it exists BOX_API void Box_callRecursiveEngineNodeLiteral(Box_EngineNode* node, Toy_Interpreter* interpreter, Toy_Literal key, Toy_LiteralArray* args); -BOX_API void Box_callRecursiveEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, char* fnName, Toy_LiteralArray* args); //call "fnName" on this node, and all children, if it exists +BOX_API void Box_callRecursiveEngineNode(Box_EngineNode* node, Toy_Interpreter* interpreter, const char* fnName, Toy_LiteralArray* args); //call "fnName" on this node, and all children, if it exists -BOX_API int Box_loadTextureEngineNode(Box_EngineNode* node, char* fname); +BOX_API int Box_loadTextureEngineNode(Box_EngineNode* node, const char* fname); BOX_API void Box_freeTextureEngineNode(Box_EngineNode* node); BOX_API void Box_setRectEngineNode(Box_EngineNode* node, SDL_Rect rect); diff --git a/box/lib_about.c b/box/lib_about.c new file mode 100644 index 0000000..fe5b0c8 --- /dev/null +++ b/box/lib_about.c @@ -0,0 +1,162 @@ +#include "lib_about.h" + +#include "toy_memory.h" + +int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) { + //the about keys + Toy_Literal majorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("major")); + Toy_Literal minorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("minor")); + Toy_Literal patchKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("patch")); + Toy_Literal buildKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("build")); + Toy_Literal authorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("author")); + + //the about identifiers + Toy_Literal majorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("major")); + Toy_Literal minorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("minor")); + Toy_Literal patchIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("patch")); + Toy_Literal buildIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("build")); + Toy_Literal authorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("author")); + + //the about values + Toy_Literal majorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MAJOR); + Toy_Literal minorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MINOR); + Toy_Literal patchLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_PATCH); + Toy_Literal buildLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(TOY_VERSION_BUILD)); + Toy_Literal authorLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("Kayne Ruse, KR Game Studios")); + + //store as an aliased dictionary + if (!TOY_IS_NULL(alias)) { + //make sure the name isn't taken + if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) { + interpreter->errorOutput("Can't override an existing variable\n"); + Toy_freeLiteral(alias); + + Toy_freeLiteral(majorKeyLiteral); + Toy_freeLiteral(minorKeyLiteral); + Toy_freeLiteral(patchKeyLiteral); + Toy_freeLiteral(buildKeyLiteral); + Toy_freeLiteral(authorKeyLiteral); + + Toy_freeLiteral(majorIdentifierLiteral); + Toy_freeLiteral(minorIdentifierLiteral); + Toy_freeLiteral(patchIdentifierLiteral); + Toy_freeLiteral(buildIdentifierLiteral); + Toy_freeLiteral(authorIdentifierLiteral); + + Toy_freeLiteral(majorLiteral); + Toy_freeLiteral(minorLiteral); + Toy_freeLiteral(patchLiteral); + Toy_freeLiteral(buildLiteral); + Toy_freeLiteral(authorLiteral); + + return -1; + } + + //create the dictionary to load up with values + Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1); + Toy_initLiteralDictionary(dictionary); + + //set each key/value pair + Toy_setLiteralDictionary(dictionary, majorKeyLiteral, majorLiteral); + Toy_setLiteralDictionary(dictionary, minorKeyLiteral, minorLiteral); + Toy_setLiteralDictionary(dictionary, patchKeyLiteral, patchLiteral); + Toy_setLiteralDictionary(dictionary, buildKeyLiteral, buildLiteral); + Toy_setLiteralDictionary(dictionary, authorKeyLiteral, authorLiteral); + + //build the type + Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true); + Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true); + Toy_Literal anyType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, true); + TOY_TYPE_PUSH_SUBTYPE(&type, strType); + TOY_TYPE_PUSH_SUBTYPE(&type, anyType); + + //set scope + Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary); + Toy_declareScopeVariable(interpreter->scope, alias, type); + Toy_setScopeVariable(interpreter->scope, alias, dict, false); + + //cleanup + Toy_freeLiteral(dict); + Toy_freeLiteral(type); + } + + //store globally + else { + //make sure the names aren't taken + if (Toy_isDelcaredScopeVariable(interpreter->scope, majorKeyLiteral) || + Toy_isDelcaredScopeVariable(interpreter->scope, minorKeyLiteral) || + Toy_isDelcaredScopeVariable(interpreter->scope, patchKeyLiteral) || + Toy_isDelcaredScopeVariable(interpreter->scope, buildKeyLiteral) || + Toy_isDelcaredScopeVariable(interpreter->scope, authorKeyLiteral)) { + interpreter->errorOutput("Can't override an existing variable\n"); + Toy_freeLiteral(alias); + + Toy_freeLiteral(majorKeyLiteral); + Toy_freeLiteral(minorKeyLiteral); + Toy_freeLiteral(patchKeyLiteral); + Toy_freeLiteral(buildKeyLiteral); + Toy_freeLiteral(authorKeyLiteral); + + Toy_freeLiteral(majorIdentifierLiteral); + Toy_freeLiteral(minorIdentifierLiteral); + Toy_freeLiteral(patchIdentifierLiteral); + Toy_freeLiteral(buildIdentifierLiteral); + Toy_freeLiteral(authorIdentifierLiteral); + + Toy_freeLiteral(majorLiteral); + Toy_freeLiteral(minorLiteral); + Toy_freeLiteral(patchLiteral); + Toy_freeLiteral(buildLiteral); + Toy_freeLiteral(authorLiteral); + + return -1; + } + + Toy_Literal intType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_INTEGER, true); + Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true); + + //major + Toy_declareScopeVariable(interpreter->scope, majorIdentifierLiteral, intType); + Toy_setScopeVariable(interpreter->scope, majorIdentifierLiteral, majorLiteral, false); + + //minor + Toy_declareScopeVariable(interpreter->scope, minorIdentifierLiteral, intType); + Toy_setScopeVariable(interpreter->scope, minorIdentifierLiteral, minorLiteral, false); + + //patch + Toy_declareScopeVariable(interpreter->scope, patchIdentifierLiteral, intType); + Toy_setScopeVariable(interpreter->scope, patchIdentifierLiteral, patchLiteral, false); + + //build + Toy_declareScopeVariable(interpreter->scope, buildIdentifierLiteral, strType); + Toy_setScopeVariable(interpreter->scope, buildIdentifierLiteral, buildLiteral, false); + + //author + Toy_declareScopeVariable(interpreter->scope, authorIdentifierLiteral, strType); + Toy_setScopeVariable(interpreter->scope, authorIdentifierLiteral, authorLiteral, false); + + Toy_freeLiteral(intType); + Toy_freeLiteral(strType); + } + + //cleanup + Toy_freeLiteral(majorKeyLiteral); + Toy_freeLiteral(minorKeyLiteral); + Toy_freeLiteral(patchKeyLiteral); + Toy_freeLiteral(buildKeyLiteral); + Toy_freeLiteral(authorKeyLiteral); + + Toy_freeLiteral(majorIdentifierLiteral); + Toy_freeLiteral(minorIdentifierLiteral); + Toy_freeLiteral(patchIdentifierLiteral); + Toy_freeLiteral(buildIdentifierLiteral); + Toy_freeLiteral(authorIdentifierLiteral); + + Toy_freeLiteral(majorLiteral); + Toy_freeLiteral(minorLiteral); + Toy_freeLiteral(patchLiteral); + Toy_freeLiteral(buildLiteral); + Toy_freeLiteral(authorLiteral); + + return 0; +} diff --git a/box/lib_about.h b/box/lib_about.h new file mode 100644 index 0000000..26b1d4a --- /dev/null +++ b/box/lib_about.h @@ -0,0 +1,6 @@ +#pragma once + +#include "toy_interpreter.h" + +int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias); + diff --git a/box/lib_compound.c b/box/lib_compound.c new file mode 100644 index 0000000..52eedc4 --- /dev/null +++ b/box/lib_compound.c @@ -0,0 +1,1003 @@ +#include "lib_compound.h" + +#include "toy_memory.h" + +#include +#include + +static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + //no arguments + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments to _concat\n"); + return -1; + } + + //get the args + Toy_Literal otherLiteral = Toy_popLiteralArray(arguments); + Toy_Literal selfLiteral = Toy_popLiteralArray(arguments); + + //parse to value if needed + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + Toy_Literal otherLiteralIdn = otherLiteral; + if (TOY_IS_IDENTIFIER(otherLiteral) && Toy_parseIdentifierToValue(interpreter, &otherLiteral)) { + Toy_freeLiteral(otherLiteralIdn); + } + + //for each self type + if (TOY_IS_ARRAY(selfLiteral)) { + if (!TOY_IS_ARRAY(otherLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _concat (unknown type for other)\n"); + Toy_freeLiteral(selfLiteral); + Toy_freeLiteral(otherLiteral); + return -1; + } + + //append each element of other to self, one-by-one + for (int i = 0; i < TOY_AS_ARRAY(otherLiteral)->count; i++) { + Toy_pushLiteralArray(TOY_AS_ARRAY(selfLiteral), TOY_AS_ARRAY(otherLiteral)->literals[i]); + } + + //return and clean up + Toy_pushLiteralArray(&interpreter->stack, selfLiteral); + + Toy_freeLiteral(selfLiteral); + Toy_freeLiteral(otherLiteral); + + return 1; + } + + if (TOY_IS_DICTIONARY(selfLiteral)) { + if (!TOY_IS_DICTIONARY(otherLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _concat (unknown type for other)\n"); + Toy_freeLiteral(selfLiteral); + Toy_freeLiteral(otherLiteral); + return -1; + } + + //append each element of self to other, which will overwrite existing entries + for (int i = 0; i < TOY_AS_DICTIONARY(selfLiteral)->capacity; i++) { + if (!TOY_IS_NULL(TOY_AS_DICTIONARY(selfLiteral)->entries[i].key)) { + Toy_setLiteralDictionary(TOY_AS_DICTIONARY(otherLiteral), TOY_AS_DICTIONARY(selfLiteral)->entries[i].key, TOY_AS_DICTIONARY(selfLiteral)->entries[i].value); + } + } + + //return and clean up + Toy_pushLiteralArray(&interpreter->stack, otherLiteral); + + Toy_freeLiteral(selfLiteral); + Toy_freeLiteral(otherLiteral); + + return 1; + } + + if (TOY_IS_STRING(selfLiteral)) { //a little redundant + if (!TOY_IS_STRING(otherLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _concat (unknown type for other)\n"); + Toy_freeLiteral(selfLiteral); + Toy_freeLiteral(otherLiteral); + return -1; + } + + //get the combined length for the new string + int length = TOY_AS_STRING(selfLiteral)->length + TOY_AS_STRING(otherLiteral)->length + 1; + + if (length > TOY_MAX_STRING_LENGTH) { + interpreter->errorOutput("Can't concatenate these strings, result is too long (error found in _concat)\n"); + Toy_freeLiteral(selfLiteral); + Toy_freeLiteral(otherLiteral); + return -1; + } + + //allocate the space and generate + char* buffer = TOY_ALLOCATE(char, length); + snprintf(buffer, length, "%s%s", Toy_toCString(TOY_AS_STRING(selfLiteral)), Toy_toCString(TOY_AS_STRING(otherLiteral))); + + Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefString(buffer)); + + //push and clean up + Toy_pushLiteralArray(&interpreter->stack, result); + + TOY_FREE_ARRAY(char, buffer, length); + Toy_freeLiteral(selfLiteral); + Toy_freeLiteral(otherLiteral); + Toy_freeLiteral(result); + + return 1; + } + + interpreter->errorOutput("Incorrect argument type passed to _concat (unknown type for self)\n"); + Toy_freeLiteral(selfLiteral); + Toy_freeLiteral(otherLiteral); + return -1; +} + +static int nativeForEach(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + //no arguments + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments to _forEach\n"); + return -1; + } + + //get the args + Toy_Literal fnLiteral = Toy_popLiteralArray(arguments); + Toy_Literal selfLiteral = Toy_popLiteralArray(arguments); + + //parse to value if needed + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + Toy_Literal fnLiteralIdn = fnLiteral; + if (TOY_IS_IDENTIFIER(fnLiteral) && Toy_parseIdentifierToValue(interpreter, &fnLiteral)) { + Toy_freeLiteral(fnLiteralIdn); + } + + //check type + if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) { + interpreter->errorOutput("Incorrect argument type passed to _forEach\n"); + Toy_freeLiteral(selfLiteral); + return -1; + } + + //call the given function on each element, based on the compound type + if (TOY_IS_ARRAY(selfLiteral)) { + for (int i = 0; i < TOY_AS_ARRAY(selfLiteral)->count; i++) { + Toy_Literal indexLiteral = TOY_TO_INTEGER_LITERAL(i); + + Toy_LiteralArray arguments; + Toy_initLiteralArray(&arguments); + Toy_pushLiteralArray(&arguments, TOY_AS_ARRAY(selfLiteral)->literals[i]); + Toy_pushLiteralArray(&arguments, indexLiteral); + + Toy_LiteralArray returns; + Toy_initLiteralArray(&returns); + + Toy_callLiteralFn(interpreter, fnLiteral, &arguments, &returns); + + Toy_freeLiteralArray(&arguments); + Toy_freeLiteralArray(&returns); + Toy_freeLiteral(indexLiteral); + } + } + + if (TOY_IS_DICTIONARY(selfLiteral)) { + for (int i = 0; i < TOY_AS_DICTIONARY(selfLiteral)->capacity; i++) { + //skip nulls + if (TOY_IS_NULL(TOY_AS_DICTIONARY(selfLiteral)->entries[i].key)) { + continue; + } + + Toy_LiteralArray arguments; + Toy_initLiteralArray(&arguments); + Toy_pushLiteralArray(&arguments, TOY_AS_DICTIONARY(selfLiteral)->entries[i].value); + Toy_pushLiteralArray(&arguments, TOY_AS_DICTIONARY(selfLiteral)->entries[i].key); + + Toy_LiteralArray returns; + Toy_initLiteralArray(&returns); + + Toy_callLiteralFn(interpreter, fnLiteral, &arguments, &returns); + + Toy_freeLiteralArray(&arguments); + Toy_freeLiteralArray(&returns); + } + } + + Toy_freeLiteral(fnLiteral); + Toy_freeLiteral(selfLiteral); + + return 0; +} + +static int nativeGetKeys(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _getKeys\n"); + return -1; + } + + //get the self + Toy_Literal selfLiteral = Toy_popLiteralArray(arguments); + + //parse to value if needed + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + //check type + if (!TOY_IS_DICTIONARY(selfLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _getKeys\n"); + Toy_freeLiteral(selfLiteral); + return -1; + } + + //generate the result literal + Toy_LiteralArray* resultPtr = TOY_ALLOCATE(Toy_LiteralArray, 1); + Toy_initLiteralArray(resultPtr); + + //get each key from the dictionary, pass it to the array + for (int i = 0; i < TOY_AS_DICTIONARY(selfLiteral)->capacity; i++) { + if (!TOY_IS_NULL( TOY_AS_DICTIONARY(selfLiteral)->entries[i].key )) { + Toy_pushLiteralArray(resultPtr, TOY_AS_DICTIONARY(selfLiteral)->entries[i].key); + } + } + + //return the result + Toy_Literal result = TOY_TO_ARRAY_LITERAL(resultPtr); //no copy + Toy_pushLiteralArray(&interpreter->stack, result); //internal copy + + //clean up + Toy_freeLiteralArray(resultPtr); + TOY_FREE(Toy_LiteralArray, resultPtr); + Toy_freeLiteral(selfLiteral); + + return 1; +} + +static int nativeGetValues(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _getValues\n"); + return -1; + } + + //get the self + Toy_Literal selfLiteral = Toy_popLiteralArray(arguments); + + //parse to value if needed + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + //check type + if (!TOY_IS_DICTIONARY(selfLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _getValues\n"); + Toy_freeLiteral(selfLiteral); + return -1; + } + + //generate the result literal + Toy_LiteralArray* resultPtr = TOY_ALLOCATE(Toy_LiteralArray, 1); + Toy_initLiteralArray(resultPtr); + + //get each key from the dictionary, pass it to the array + for (int i = 0; i < TOY_AS_DICTIONARY(selfLiteral)->capacity; i++) { + if (!TOY_IS_NULL( TOY_AS_DICTIONARY(selfLiteral)->entries[i].key )) { + Toy_pushLiteralArray(resultPtr, TOY_AS_DICTIONARY(selfLiteral)->entries[i].value); + } + } + + //return the result + Toy_Literal result = TOY_TO_ARRAY_LITERAL(resultPtr); //no copy + Toy_pushLiteralArray(&interpreter->stack, result); //internal copy + + //clean up + Toy_freeLiteralArray(resultPtr); + TOY_FREE(Toy_LiteralArray, resultPtr); + Toy_freeLiteral(selfLiteral); + + return 1; +} + +static int nativeMap(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + //no arguments + if (arguments->count != 2) { + interpreter->errorOutput("Incorrect number of arguments to _map\n"); + return -1; + } + + //get the args + Toy_Literal fnLiteral = Toy_popLiteralArray(arguments); + Toy_Literal selfLiteral = Toy_popLiteralArray(arguments); + + //parse to value if needed + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + Toy_Literal fnLiteralIdn = fnLiteral; + if (TOY_IS_IDENTIFIER(fnLiteral) && Toy_parseIdentifierToValue(interpreter, &fnLiteral)) { + Toy_freeLiteral(fnLiteralIdn); + } + + //check type + if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) { + interpreter->errorOutput("Incorrect argument type passed to _map\n"); + Toy_freeLiteral(selfLiteral); + return -1; + } + + //call the given function on each element, based on the compound type + if (TOY_IS_ARRAY(selfLiteral)) { + Toy_LiteralArray* returnsPtr = TOY_ALLOCATE(Toy_LiteralArray, 1); + Toy_initLiteralArray(returnsPtr); + + for (int i = 0; i < TOY_AS_ARRAY(selfLiteral)->count; i++) { + Toy_Literal indexLiteral = TOY_TO_INTEGER_LITERAL(i); + + Toy_LiteralArray arguments; + Toy_initLiteralArray(&arguments); + Toy_pushLiteralArray(&arguments, TOY_AS_ARRAY(selfLiteral)->literals[i]); + Toy_pushLiteralArray(&arguments, indexLiteral); + + Toy_LiteralArray returns; + Toy_initLiteralArray(&returns); + + Toy_callLiteralFn(interpreter, fnLiteral, &arguments, &returns); + + //grab the results + Toy_Literal lit = Toy_popLiteralArray(&returns); + Toy_pushLiteralArray(returnsPtr, lit); + Toy_freeLiteral(lit); + + Toy_freeLiteralArray(&arguments); + Toy_freeLiteralArray(&returns); + Toy_freeLiteral(indexLiteral); + } + + Toy_Literal returnsLiteral = TOY_TO_ARRAY_LITERAL(returnsPtr); + Toy_pushLiteralArray(&interpreter->stack, returnsLiteral); + Toy_freeLiteral(returnsLiteral); + } + + if (TOY_IS_DICTIONARY(selfLiteral)) { + Toy_LiteralArray* returnsPtr = TOY_ALLOCATE(Toy_LiteralArray, 1); + Toy_initLiteralArray(returnsPtr); + + for (int i = 0; i < TOY_AS_DICTIONARY(selfLiteral)->capacity; i++) { + //skip nulls + if (TOY_IS_NULL(TOY_AS_DICTIONARY(selfLiteral)->entries[i].key)) { + continue; + } + + Toy_LiteralArray arguments; + Toy_initLiteralArray(&arguments); + Toy_pushLiteralArray(&arguments, TOY_AS_DICTIONARY(selfLiteral)->entries[i].value); + Toy_pushLiteralArray(&arguments, TOY_AS_DICTIONARY(selfLiteral)->entries[i].key); + + Toy_LiteralArray returns; + Toy_initLiteralArray(&returns); + + Toy_callLiteralFn(interpreter, fnLiteral, &arguments, &returns); + + //grab the results + Toy_Literal lit = Toy_popLiteralArray(&returns); + Toy_pushLiteralArray(returnsPtr, lit); + Toy_freeLiteral(lit); + + Toy_freeLiteralArray(&arguments); + Toy_freeLiteralArray(&returns); + } + + Toy_Literal returnsLiteral = TOY_TO_ARRAY_LITERAL(returnsPtr); + Toy_pushLiteralArray(&interpreter->stack, returnsLiteral); + Toy_freeLiteral(returnsLiteral); + } + + Toy_freeLiteral(fnLiteral); + Toy_freeLiteral(selfLiteral); + + return 0; +} + +static int nativeReduce(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + //no arguments + if (arguments->count != 3) { + interpreter->errorOutput("Incorrect number of arguments to _reduce\n"); + return -1; + } + + //get the args + Toy_Literal fnLiteral = Toy_popLiteralArray(arguments); + Toy_Literal defaultLiteral = Toy_popLiteralArray(arguments); + Toy_Literal selfLiteral = Toy_popLiteralArray(arguments); + + //parse to value if needed + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + Toy_Literal defaultLiteralIdn = defaultLiteral; + if (TOY_IS_IDENTIFIER(defaultLiteral) && Toy_parseIdentifierToValue(interpreter, &defaultLiteral)) { + Toy_freeLiteral(defaultLiteralIdn); + } + + Toy_Literal fnLiteralIdn = fnLiteral; + if (TOY_IS_IDENTIFIER(fnLiteral) && Toy_parseIdentifierToValue(interpreter, &fnLiteral)) { + Toy_freeLiteral(fnLiteralIdn); + } + + //check type + if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) { + interpreter->errorOutput("Incorrect argument type passed to _reduce\n"); + Toy_freeLiteral(selfLiteral); + return -1; + } + + //call the given function on each element, based on the compound type + if (TOY_IS_ARRAY(selfLiteral)) { + for (int i = 0; i < TOY_AS_ARRAY(selfLiteral)->count; i++) { + Toy_Literal indexLiteral = TOY_TO_INTEGER_LITERAL(i); + + Toy_LiteralArray arguments; + Toy_initLiteralArray(&arguments); + Toy_pushLiteralArray(&arguments, TOY_AS_ARRAY(selfLiteral)->literals[i]); + Toy_pushLiteralArray(&arguments, indexLiteral); + Toy_pushLiteralArray(&arguments, defaultLiteral); + + Toy_LiteralArray returns; + Toy_initLiteralArray(&returns); + + Toy_callLiteralFn(interpreter, fnLiteral, &arguments, &returns); + + //grab the results + Toy_freeLiteral(defaultLiteral); + defaultLiteral = Toy_popLiteralArray(&returns); + + Toy_freeLiteralArray(&arguments); + Toy_freeLiteralArray(&returns); + Toy_freeLiteral(indexLiteral); + } + + Toy_pushLiteralArray(&interpreter->stack, defaultLiteral); + } + + if (TOY_IS_DICTIONARY(selfLiteral)) { + for (int i = 0; i < TOY_AS_DICTIONARY(selfLiteral)->capacity; i++) { + //skip nulls + if (TOY_IS_NULL(TOY_AS_DICTIONARY(selfLiteral)->entries[i].key)) { + continue; + } + + Toy_LiteralArray arguments; + Toy_initLiteralArray(&arguments); + Toy_pushLiteralArray(&arguments, TOY_AS_DICTIONARY(selfLiteral)->entries[i].value); + Toy_pushLiteralArray(&arguments, TOY_AS_DICTIONARY(selfLiteral)->entries[i].key); + Toy_pushLiteralArray(&arguments, defaultLiteral); + + Toy_LiteralArray returns; + Toy_initLiteralArray(&returns); + + Toy_callLiteralFn(interpreter, fnLiteral, &arguments, &returns); + + //grab the results + Toy_freeLiteral(defaultLiteral); + defaultLiteral = Toy_popLiteralArray(&returns); + + Toy_freeLiteralArray(&arguments); + Toy_freeLiteralArray(&returns); + } + + Toy_pushLiteralArray(&interpreter->stack, defaultLiteral); + } + + Toy_freeLiteral(fnLiteral); + Toy_freeLiteral(defaultLiteral); + Toy_freeLiteral(selfLiteral); + + return 0; +} + +static int nativeToLower(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _toLower\n"); + return -1; + } + + //get the argument to a C-string + Toy_Literal selfLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + if (!TOY_IS_STRING(selfLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _toLower\n"); + Toy_freeLiteral(selfLiteral); + return -1; + } + + Toy_RefString* selfRefString = TOY_AS_STRING(selfLiteral); + const char* self = Toy_toCString(selfRefString); + + //allocate buffer space for the result + char* result = TOY_ALLOCATE(char, Toy_lengthRefString(selfRefString) + 1); + + //set each new character + for (int i = 0; i < (int)Toy_lengthRefString(selfRefString); i++) { + result[i] = tolower(self[i]); + } + result[Toy_lengthRefString(selfRefString)] = '\0'; //end the string + + //wrap up and push the new result onto the stack + Toy_RefString* resultRefString = Toy_createRefStringLength(result, Toy_lengthRefString(selfRefString)); //internal copy + Toy_Literal resultLiteral = TOY_TO_STRING_LITERAL(resultRefString); //NO copy + + Toy_pushLiteralArray(&interpreter->stack, resultLiteral); //internal copy + + //cleanup + TOY_FREE_ARRAY(char, result, Toy_lengthRefString(resultRefString) + 1); + Toy_freeLiteral(resultLiteral); + Toy_freeLiteral(selfLiteral); + + return 1; +} + +static char* toStringUtilObject = NULL; +static void toStringUtil(const char* input) { + size_t len = strlen(input) + 1; + + if (len > TOY_MAX_STRING_LENGTH) { + len = TOY_MAX_STRING_LENGTH; //TODO: don't truncate + } + + toStringUtilObject = TOY_ALLOCATE(char, len); + + snprintf(toStringUtilObject, len, "%s", input); +} + +static int nativeToString(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _toString\n"); + return -1; + } + + //get the argument + Toy_Literal selfLiteral = Toy_popLiteralArray(arguments); + + //parse to a value + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + //BUGFIX: probably an undefined variable + if (TOY_IS_IDENTIFIER(selfLiteral)) { + Toy_freeLiteral(selfLiteral); + return -1; + } + + //print it to a custom function + Toy_printLiteralCustom(selfLiteral, toStringUtil); + + //create the resulting string and push it + Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefString(toStringUtilObject)); //internal copy + + Toy_pushLiteralArray(&interpreter->stack, result); + + //cleanup + TOY_FREE_ARRAY(char, toStringUtilObject, Toy_lengthRefString( TOY_AS_STRING(result) ) + 1); + toStringUtilObject = NULL; + + Toy_freeLiteral(result); + Toy_freeLiteral(selfLiteral); + + return 1; +} + +static int nativeToUpper(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + //no arguments + if (arguments->count != 1) { + interpreter->errorOutput("Incorrect number of arguments to _toUpper\n"); + return -1; + } + + //get the argument to a C-string + Toy_Literal selfLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + if (!TOY_IS_STRING(selfLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _toUpper\n"); + Toy_freeLiteral(selfLiteral); + return -1; + } + + Toy_RefString* selfRefString = TOY_AS_STRING(selfLiteral); + const char* self = Toy_toCString(selfRefString); + + //allocate buffer space for the result + char* result = TOY_ALLOCATE(char, Toy_lengthRefString(selfRefString) + 1); + + //set each new character + for (int i = 0; i < (int)Toy_lengthRefString(selfRefString); i++) { + result[i] = toupper(self[i]); + } + result[Toy_lengthRefString(selfRefString)] = '\0'; //end the string + + //wrap up and push the new result onto the stack + Toy_RefString* resultRefString = Toy_createRefStringLength(result, Toy_lengthRefString(selfRefString)); //internal copy + Toy_Literal resultLiteral = TOY_TO_STRING_LITERAL(resultRefString); //NO copy + + Toy_pushLiteralArray(&interpreter->stack, resultLiteral); //internal copy + + //cleanup + TOY_FREE_ARRAY(char, result, Toy_lengthRefString(resultRefString) + 1); + Toy_freeLiteral(resultLiteral); + Toy_freeLiteral(selfLiteral); + + return 1; +} + +static int nativeTrim(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + if (arguments->count < 1 || arguments->count > 2) { + interpreter->errorOutput("Incorrect number of arguments to _trim\n"); + return -1; + } + + //get the arguments + Toy_Literal trimCharsLiteral; + Toy_Literal selfLiteral; + + if (arguments->count == 2) { + trimCharsLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal trimCharsLiteralIdn = trimCharsLiteral; + if (TOY_IS_IDENTIFIER(trimCharsLiteral) && Toy_parseIdentifierToValue(interpreter, &trimCharsLiteral)) { + Toy_freeLiteral(trimCharsLiteralIdn); + } + } + else { + trimCharsLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(" \t\n\r")); + } + selfLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + if (!TOY_IS_STRING(selfLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _trim\n"); + Toy_freeLiteral(trimCharsLiteral); + Toy_freeLiteral(selfLiteral); + return -1; + } + + //unwrap the arguments + Toy_RefString* trimCharsRefString = TOY_AS_STRING(trimCharsLiteral); + Toy_RefString* selfRefString = TOY_AS_STRING(selfLiteral); + + //allocate space for the new string + int bufferBegin = 0; + int bufferEnd = Toy_lengthRefString(selfRefString); + + //for each character in self, check it against each character in trimChars - on a fail, go to end + for (int i = 0; i < (int)Toy_lengthRefString(selfRefString); i++) { + int trimIndex = 0; + + while (trimIndex < (int)Toy_lengthRefString(trimCharsRefString)) { + //there is a match - DON'T increment anymore + if (Toy_toCString(selfRefString)[i] == Toy_toCString(trimCharsRefString)[trimIndex]) { + break; + } + + trimIndex++; + } + + //if the match is found, increment buffer begin + if (trimIndex < (int)Toy_lengthRefString(trimCharsRefString)) { + bufferBegin++; + continue; + } + else { + break; + } + } + + //again, from the back + for (int i = Toy_lengthRefString(selfRefString); i >= 0; i--) { + int trimIndex = 0; + + while (trimIndex < (int)Toy_lengthRefString(trimCharsRefString)) { + //there is a match - DON'T increment anymore + if (Toy_toCString(selfRefString)[i-1] == Toy_toCString(trimCharsRefString)[trimIndex]) { + break; + } + + trimIndex++; + } + + //if the match is found, increment buffer begin + if (trimIndex < (int)Toy_lengthRefString(trimCharsRefString)) { + bufferEnd--; + continue; + } + else { + break; + } + } + + //generate the result + Toy_Literal resultLiteral; + if (bufferBegin >= bufferEnd) { //catch errors + resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("")); + } + else { + char buffer[TOY_MAX_STRING_LENGTH]; + snprintf(buffer, bufferEnd - bufferBegin + 1, "%s", &Toy_toCString(selfRefString)[ bufferBegin ]); + resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(buffer)); //internal copy + } + + //wrap up the buffer and return it + Toy_pushLiteralArray(&interpreter->stack, resultLiteral); //internal copy + + //cleanup + Toy_freeLiteral(resultLiteral); + Toy_freeLiteral(trimCharsLiteral); + Toy_freeLiteral(selfLiteral); + + return 1; +} + +static int nativeTrimBegin(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + if (arguments->count < 1 || arguments->count > 2) { + interpreter->errorOutput("Incorrect number of arguments to _trimBegin\n"); + return -1; + } + + //get the arguments + Toy_Literal trimCharsLiteral; + Toy_Literal selfLiteral; + + if (arguments->count == 2) { + trimCharsLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal trimCharsLiteralIdn = trimCharsLiteral; + if (TOY_IS_IDENTIFIER(trimCharsLiteral) && Toy_parseIdentifierToValue(interpreter, &trimCharsLiteral)) { + Toy_freeLiteral(trimCharsLiteralIdn); + } + } + else { + trimCharsLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(" \t\n\r")); + } + selfLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + if (!TOY_IS_STRING(selfLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _trimBegin\n"); + Toy_freeLiteral(trimCharsLiteral); + Toy_freeLiteral(selfLiteral); + return -1; + } + + //unwrap the arguments + Toy_RefString* trimCharsRefString = TOY_AS_STRING(trimCharsLiteral); + Toy_RefString* selfRefString = TOY_AS_STRING(selfLiteral); + + //allocate space for the new string + int bufferBegin = 0; + int bufferEnd = Toy_lengthRefString(selfRefString); + + //for each character in self, check it against each character in trimChars - on a fail, go to end + for (int i = 0; i < (int)Toy_lengthRefString(selfRefString); i++) { + int trimIndex = 0; + + while (trimIndex < (int)Toy_lengthRefString(trimCharsRefString)) { + //there is a match - DON'T increment anymore + if (Toy_toCString(selfRefString)[i] == Toy_toCString(trimCharsRefString)[trimIndex]) { + break; + } + + trimIndex++; + } + + //if the match is found, increment buffer begin + if (trimIndex < (int)Toy_lengthRefString(trimCharsRefString)) { + bufferBegin++; + continue; + } + else { + break; + } + } + + //generate the result + Toy_Literal resultLiteral; + if (bufferBegin >= bufferEnd) { //catch errors + resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("")); + } + else { + char buffer[TOY_MAX_STRING_LENGTH]; + snprintf(buffer, bufferEnd - bufferBegin + 1, "%s", &Toy_toCString(selfRefString)[ bufferBegin ]); + resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(buffer)); //internal copy + } + + //wrap up the buffer and return it + Toy_pushLiteralArray(&interpreter->stack, resultLiteral); //internal copy + + //cleanup + Toy_freeLiteral(resultLiteral); + Toy_freeLiteral(trimCharsLiteral); + Toy_freeLiteral(selfLiteral); + + return 1; +} + +static int nativeTrimEnd(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + if (arguments->count < 1 || arguments->count > 2) { + interpreter->errorOutput("Incorrect number of arguments to _trimEnd\n"); + return -1; + } + + //get the arguments + Toy_Literal trimCharsLiteral; + Toy_Literal selfLiteral; + + if (arguments->count == 2) { + trimCharsLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal trimCharsLiteralIdn = trimCharsLiteral; + if (TOY_IS_IDENTIFIER(trimCharsLiteral) && Toy_parseIdentifierToValue(interpreter, &trimCharsLiteral)) { + Toy_freeLiteral(trimCharsLiteralIdn); + } + } + else { + trimCharsLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(" \t\n\r")); + } + selfLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + if (!TOY_IS_STRING(selfLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _trimEnd\n"); + Toy_freeLiteral(trimCharsLiteral); + Toy_freeLiteral(selfLiteral); + return -1; + } + + //unwrap the arguments + Toy_RefString* trimCharsRefString = TOY_AS_STRING(trimCharsLiteral); + Toy_RefString* selfRefString = TOY_AS_STRING(selfLiteral); + + //allocate space for the new string + int bufferBegin = 0; + int bufferEnd = Toy_lengthRefString(selfRefString); + + //again, from the back + for (int i = (int)Toy_lengthRefString(selfRefString); i >= 0; i--) { + int trimIndex = 0; + + while (trimIndex < (int)Toy_lengthRefString(trimCharsRefString)) { + //there is a match - DON'T increment anymore + if (Toy_toCString(selfRefString)[i-1] == Toy_toCString(trimCharsRefString)[trimIndex]) { + break; + } + + trimIndex++; + } + + //if the match is found, increment buffer begin + if (trimIndex < (int)Toy_lengthRefString(trimCharsRefString)) { + bufferEnd--; + continue; + } + else { + break; + } + } + + //generate the result + Toy_Literal resultLiteral; + if (bufferBegin >= bufferEnd) { //catch errors + resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("")); + } + else { + char buffer[TOY_MAX_STRING_LENGTH]; + snprintf(buffer, bufferEnd - bufferBegin + 1, "%s", &Toy_toCString(selfRefString)[ bufferBegin ]); + resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(buffer)); //internal copy + } + + //wrap up the buffer and return it + Toy_pushLiteralArray(&interpreter->stack, resultLiteral); //internal copy + + //cleanup + Toy_freeLiteral(resultLiteral); + Toy_freeLiteral(trimCharsLiteral); + Toy_freeLiteral(selfLiteral); + + return 1; +} + +//call the hook +typedef struct Natives { + char* name; + Toy_NativeFn fn; +} Natives; + +int Toy_hookCompound(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) { + //build the natives list + Natives natives[] = { + {"_concat", nativeConcat}, //array, dictionary, string + // {"_containsKey", native}, //dictionary + // {"_containsValue", native}, //array, dictionary + // {"_every", native}, //array, dictionary, string + // {"_filter", native}, //array, dictionary + {"_forEach", nativeForEach}, //array, dictionary + {"_getKeys", nativeGetKeys}, //dictionary + {"_getValues", nativeGetValues}, //dictionary + // {"_indexOf", native}, //array, string + // {"_insert", native}, //array, dictionary, string + {"_map", nativeMap}, //array, dictionary + {"_reduce", nativeReduce}, //array, dictionary + // {"_remove", native}, //array, dictionary + // {"_replace", native}, //string + // {"_some", native}, //array, dictionary, string + // {"_sort", native}, //array + {"_toLower", nativeToLower}, //string + {"_toString", nativeToString}, //array, dictionary + {"_toUpper", nativeToUpper}, //string + {"_trim", nativeTrim}, //string + {"_trimBegin", nativeTrimBegin}, //string + {"_trimEnd", nativeTrimEnd}, //string + {NULL, NULL} + }; + + //store the library in an aliased dictionary + if (!TOY_IS_NULL(alias)) { + //make sure the name isn't taken + if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) { + interpreter->errorOutput("Can't override an existing variable\n"); + Toy_freeLiteral(alias); + return -1; + } + + //create the dictionary to load up with functions + Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1); + Toy_initLiteralDictionary(dictionary); + + //load the dict with functions + for (int i = 0; natives[i].name; i++) { + Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name)); + Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn); + + Toy_setLiteralDictionary(dictionary, name, func); + + Toy_freeLiteral(name); + Toy_freeLiteral(func); + } + + //build the type + Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true); + Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true); + Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true); + TOY_TYPE_PUSH_SUBTYPE(&type, strType); + TOY_TYPE_PUSH_SUBTYPE(&type, fnType); + + //set scope + Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary); + Toy_declareScopeVariable(interpreter->scope, alias, type); + Toy_setScopeVariable(interpreter->scope, alias, dict, false); + + //cleanup + Toy_freeLiteral(dict); + Toy_freeLiteral(type); + return 0; + } + + //default + for (int i = 0; natives[i].name; i++) { + Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn); + } + + return 0; +} diff --git a/box/lib_compound.h b/box/lib_compound.h new file mode 100644 index 0000000..2f256f6 --- /dev/null +++ b/box/lib_compound.h @@ -0,0 +1,6 @@ +#pragma once + +#include "toy_interpreter.h" + +int Toy_hookCompound(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias); + diff --git a/box/lib_engine.c b/box/lib_engine.c index bc870f5..cf9bcf3 100644 --- a/box/lib_engine.c +++ b/box/lib_engine.c @@ -121,8 +121,8 @@ static int nativeLoadRootNode(Toy_Interpreter* interpreter, Toy_LiteralArray* ar //load the new root node size_t size = 0; - char* source = Toy_readFile(Toy_toCString(TOY_AS_STRING(filePathLiteral)), &size); - unsigned char* tb = Toy_compileString(source, &size); + const char* source = Toy_readFile(Toy_toCString(TOY_AS_STRING(filePathLiteral)), &size); + const unsigned char* tb = Toy_compileString(source, &size); free((void*)source); engine.rootNode = TOY_ALLOCATE(Box_EngineNode, 1); diff --git a/box/lib_node.c b/box/lib_node.c index 565ebed..15d57a6 100644 --- a/box/lib_node.c +++ b/box/lib_node.c @@ -44,8 +44,8 @@ static int nativeLoadNode(Toy_Interpreter* interpreter, Toy_LiteralArray* argume //load the new node size_t size = 0; - char* source = Toy_readFile(Toy_toCString(TOY_AS_STRING(filePathLiteral)), &size); - unsigned char* tb = Toy_compileString(source, &size); + const char* source = Toy_readFile(Toy_toCString(TOY_AS_STRING(filePathLiteral)), &size); + const unsigned char* tb = Toy_compileString(source, &size); free((void*)source); Box_EngineNode* node = TOY_ALLOCATE(Box_EngineNode, 1); diff --git a/box/lib_runner.c b/box/lib_runner.c index 6ea1eba..186b6d3 100644 --- a/box/lib_runner.c +++ b/box/lib_runner.c @@ -10,7 +10,7 @@ typedef struct Toy_Runner { Toy_Interpreter interpreter; - unsigned char* bytecode; + const unsigned char* bytecode; size_t size; bool dirty; @@ -26,6 +26,12 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu //get the file path literal with a handle Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal drivePathLiteralIdn = drivePathLiteral; + if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) { + Toy_freeLiteral(drivePathLiteralIdn); + } + Toy_Literal filePathLiteral = Toy_getFilePathLiteral(interpreter, &drivePathLiteral); if (TOY_IS_NULL(filePathLiteral)) { @@ -37,12 +43,12 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu Toy_freeLiteral(drivePathLiteral); //use raw types - easier - char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral)); + const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral)); int filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral)); //load and compile the bytecode size_t fileSize = 0; - char* source = Toy_readFile(filePath, &fileSize); + const char* source = Toy_readFile(filePath, &fileSize); if (!source) { interpreter->errorOutput("Failed to load source file\n"); @@ -50,7 +56,7 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu return -1; } - unsigned char* bytecode = Toy_compileString(source, &fileSize); + const unsigned char* bytecode = Toy_compileString(source, &fileSize); free((void*)source); if (!bytecode) { @@ -90,10 +96,16 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr //get the argument Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal drivePathLiteralIdn = drivePathLiteral; + if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) { + Toy_freeLiteral(drivePathLiteralIdn); + } + Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(drivePathLiteral)); //get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library - int driveLength = 0; + size_t driveLength = 0; while (Toy_toCString(drivePath)[driveLength] != ':') { if (driveLength >= Toy_lengthRefString(drivePath)) { interpreter->errorOutput("Incorrect drive path format given to loadScriptBytecode\n"); @@ -480,7 +492,7 @@ static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray //call the hook typedef struct Natives { - char* name; + const char* name; Toy_NativeFn fn; } Natives; @@ -504,7 +516,7 @@ int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) { interpreter->errorOutput("Can't override an existing variable\n"); Toy_freeLiteral(alias); - return false; + return -1; } //create the dictionary to load up with functions @@ -573,7 +585,7 @@ Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* dr Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral)); //get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library - int driveLength = 0; + size_t driveLength = 0; while (Toy_toCString(drivePath)[driveLength] != ':') { if (driveLength >= Toy_lengthRefString(drivePath)) { interpreter->errorOutput("Incorrect drive path format given to Toy_getFilePathLiteral\n"); diff --git a/box/lib_standard.c b/box/lib_standard.c index bb427b7..33d612f 100644 --- a/box/lib_standard.c +++ b/box/lib_standard.c @@ -49,7 +49,7 @@ int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_L if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) { interpreter->errorOutput("Can't override an existing variable\n"); Toy_freeLiteral(alias); - return false; + return -1; } //create the dictionary to load up with functions diff --git a/box/lib_timer.c b/box/lib_timer.c index f0d3194..d05011e 100644 --- a/box/lib_timer.c +++ b/box/lib_timer.c @@ -366,7 +366,7 @@ int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lite if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) { interpreter->errorOutput("Can't override an existing variable\n"); Toy_freeLiteral(alias); - return false; + return -1; } //create the dictionary to load up with functions diff --git a/box/repl_tools.c b/box/repl_tools.c index 0b67a57..7018e93 100644 --- a/box/repl_tools.c +++ b/box/repl_tools.c @@ -1,4 +1,6 @@ #include "repl_tools.h" +#include "lib_about.h" +#include "lib_compound.h" #include "lib_standard.h" #include "lib_timer.h" #include "lib_runner.h" @@ -14,7 +16,7 @@ #include //IO functions -char* Toy_readFile(char* path, size_t* fileSize) { +const char* Toy_readFile(const char* path, size_t* fileSize) { FILE* file = fopen(path, "rb"); if (file == NULL) { @@ -47,7 +49,7 @@ char* Toy_readFile(char* path, size_t* fileSize) { return buffer; } -int Toy_writeFile(char* path, unsigned char* bytes, size_t size) { +int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size) { FILE* file = fopen(path, "wb"); if (file == NULL) { @@ -68,7 +70,7 @@ int Toy_writeFile(char* path, unsigned char* bytes, size_t size) { } //repl functions -unsigned char* Toy_compileString(char* source, size_t* size) { +const unsigned char* Toy_compileString(const char* source, size_t* size) { Toy_Lexer lexer; Toy_Parser parser; Toy_Compiler compiler; @@ -94,7 +96,7 @@ unsigned char* Toy_compileString(char* source, size_t* size) { } //get the bytecode dump - unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size)); + const unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size)); //cleanup Toy_freeCompiler(&compiler); @@ -105,11 +107,13 @@ unsigned char* Toy_compileString(char* source, size_t* size) { return tb; } -void Toy_runBinary(unsigned char* tb, size_t size) { +void Toy_runBinary(const unsigned char* tb, size_t size) { Toy_Interpreter interpreter; Toy_initInterpreter(&interpreter); //inject the libs + Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout); + Toy_injectNativeHook(&interpreter, "compound", Toy_hookCompound); Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard); Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer); Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner); @@ -118,9 +122,9 @@ void Toy_runBinary(unsigned char* tb, size_t size) { Toy_freeInterpreter(&interpreter); } -void Toy_runBinaryFile(char* fname) { +void Toy_runBinaryFile(const char* fname) { size_t size = 0; //not used - unsigned char* tb = (unsigned char*)Toy_readFile(fname, &size); + const unsigned char* tb = (const unsigned char*)Toy_readFile(fname, &size); if (!tb) { return; } @@ -128,9 +132,9 @@ void Toy_runBinaryFile(char* fname) { //interpreter takes ownership of the binary data } -void Toy_runSource(char* source) { +void Toy_runSource(const char* source) { size_t size = 0; - unsigned char* tb = Toy_compileString(source, &size); + const unsigned char* tb = Toy_compileString(source, &size); if (!tb) { return; } @@ -138,9 +142,12 @@ void Toy_runSource(char* source) { Toy_runBinary(tb, size); } -void Toy_runSourceFile(char* fname) { +void Toy_runSourceFile(const char* fname) { size_t size = 0; //not used - char* source = Toy_readFile(fname, &size); + const char* source = Toy_readFile(fname, &size); + if (!source) { + return; + } Toy_runSource(source); free((void*)source); } diff --git a/box/repl_tools.h b/box/repl_tools.h index 57f5605..819f752 100644 --- a/box/repl_tools.h +++ b/box/repl_tools.h @@ -2,13 +2,13 @@ #include "toy_common.h" -char* Toy_readFile(char* path, size_t* fileSize); -int Toy_writeFile(char* path, unsigned char* bytes, size_t size); +const char* Toy_readFile(const char* path, size_t* fileSize); +int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size); -unsigned char* Toy_compileString(char* source, size_t* size); +const unsigned char* Toy_compileString(const char* source, size_t* size); -void Toy_runBinary(unsigned char* tb, size_t size); -void Toy_runBinaryFile(char* fname); -void Toy_runSource(char* source); -void Toy_runSourceFile(char* fname); +void Toy_runBinary(const unsigned char* tb, size_t size); +void Toy_runBinaryFile(const char* fname); +void Toy_runSource(const char* source); +void Toy_runSourceFile(const char* fname); diff --git a/test/test_engine_node.c b/test/test_engine_node.c index d696889..d6e1d8d 100644 --- a/test/test_engine_node.c +++ b/test/test_engine_node.c @@ -27,8 +27,8 @@ int main() { size_t size = 0; - char* source = Toy_readFile("./scripts/parent_engine_node.toy", &size); - unsigned char* tb = Toy_compileString(source, &size); + const char* source = Toy_readFile("./scripts/parent_engine_node.toy", &size); + const unsigned char* tb = Toy_compileString(source, &size); //create and test the engine node Box_EngineNode* node = TOY_ALLOCATE(Box_EngineNode, 1); @@ -62,8 +62,8 @@ int main() { size_t size = 0; - char* source = Toy_readFile("./scripts/parent_engine_node.toy", &size); - unsigned char* tb = Toy_compileString(source, &size); + const char* source = Toy_readFile("./scripts/parent_engine_node.toy", &size); + const unsigned char* tb = Toy_compileString(source, &size); //create and test the engine node Box_EngineNode* node = TOY_ALLOCATE(Box_EngineNode, 1); @@ -72,8 +72,8 @@ int main() { Toy_resetInterpreter(&interpreter); for (int i = 0; i < 10; i++) { - char* source = Toy_readFile("./scripts/child_engine_node.toy", &size); - unsigned char* tb = Toy_compileString(source, &size); + const char* source = Toy_readFile("./scripts/child_engine_node.toy", &size); + const unsigned char* tb = Toy_compileString(source, &size); Box_EngineNode* child = TOY_ALLOCATE(Box_EngineNode, 1); Box_initEngineNode(child, &interpreter, tb, size);