diff --git a/repl/lib_compound.c b/repl/lib_compound.c index 7c4661c..d7b54b4 100644 --- a/repl/lib_compound.c +++ b/repl/lib_compound.c @@ -115,6 +115,81 @@ static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* argument 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); + } + } + + return 0; +} + static int nativeGetKeys(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { if (arguments->count != 1) { interpreter->errorOutput("Incorrect number of arguments to _getKeys\n"); @@ -205,6 +280,105 @@ static int nativeGetValues(Toy_Interpreter* interpreter, Toy_LiteralArray* argum 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); + } + + return 0; +} + static int nativeToLower(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { //no arguments if (arguments->count != 1) { @@ -477,11 +651,12 @@ int Toy_hookCompound(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_L // {"_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", native}, //array, dictionary + {"_map", nativeMap}, //array, dictionary // {"_reduce", native}, //array, dictionary // {"_remove", native}, //array, dictionary // {"_replace", native}, //string diff --git a/scripts/small.toy b/scripts/small.toy index 2f88e8c..e69de29 100644 --- a/scripts/small.toy +++ b/scripts/small.toy @@ -1,14 +0,0 @@ -import compound; - - -//test dictionary concat with clashing keys -{ - var a = ["one" : 1, "two": 2, "three": 3, "random": 1]; - var b = ["four" : 4, "five": 5, "six": 6, "random": 2]; - - var c = a.concat(b); - - assert c["random"] == 1, "dictionary.concat() clashing keys failed"; -} - -print "All good"; diff --git a/source/toy_interpreter.c b/source/toy_interpreter.c index f3f1280..f62eb98 100644 --- a/source/toy_interpreter.c +++ b/source/toy_interpreter.c @@ -1187,6 +1187,50 @@ static bool execFnCall(Toy_Interpreter* interpreter, bool looseFirstArgument) { } bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns) { + //check for side-loaded native functions + if (TOY_IS_FUNCTION_NATIVE(func)) { + //reverse the order to the correct order + Toy_LiteralArray correct; + Toy_initLiteralArray(&correct); + + while(arguments->count) { + Toy_Literal lit = Toy_popLiteralArray(arguments); + Toy_pushLiteralArray(&correct, lit); + Toy_freeLiteral(lit); + } + + //call the native function + int returnsCount = TOY_AS_FUNCTION_NATIVE(func)(interpreter, &correct); + + if (returnsCount < 0) { + interpreter->errorOutput("Unknown error from native function\n"); + Toy_freeLiteralArray(&correct); + return false; + } + + //get the results + Toy_LiteralArray returnsFromInner; + Toy_initLiteralArray(&returnsFromInner); + + for (int i = 0; i < (returnsCount || 1); i++) { + Toy_Literal lit = Toy_popLiteralArray(&interpreter->stack); + Toy_pushLiteralArray(&returnsFromInner, lit); //NOTE: also reverses the order + Toy_freeLiteral(lit); + } + + //flip them around and pass to returns + while (returnsFromInner.count > 0) { + Toy_Literal lit = Toy_popLiteralArray(&returnsFromInner); + Toy_pushLiteralArray(returns, lit); + Toy_freeLiteral(lit); + } + + Toy_freeLiteralArray(&returnsFromInner); + Toy_freeLiteralArray(&correct); + return true; + } + + //normal Toy function if (!TOY_IS_FUNCTION(func)) { interpreter->errorOutput("Function required in Toy_callLiteralFn()\n"); return false; diff --git a/test/scripts/lib/compound.toy b/test/scripts/lib/compound.toy index 9c2a649..ecaa041 100644 --- a/test/scripts/lib/compound.toy +++ b/test/scripts/lib/compound.toy @@ -1,5 +1,6 @@ import compound; +//test concat { //test array concat { @@ -44,6 +45,26 @@ import compound; } } +//test forEach +{ + var counter = 0; + + fn p(k, v) { + counter++; + print string k + ": " + string v; + } + + var a = ["a", "b"]; + var d = ["foo": 1, "bar": 2, "bazz": 3, "fizz": 4]; + + a.forEach(p); + assert counter == 2, "forEach ran an unusual number of times"; + + counter = 0; + d.forEach(p); + assert counter == 4, "forEach ran an unusual number of times"; +} + //test getKeys { var d = ["foo": 1, "bar": 2]; @@ -70,6 +91,28 @@ import compound; } +//test map +{ + //test map with toy functions + { + fn increment(k, v) { + return v + 1; + } + + var a = [1, 2, 3]; + var d = ["four": 4, "five": 5, "six": 6]; + + assert a.map(increment).map(increment).map(increment) == [4,5,6], "array.map() failed"; + assert d.map(increment).map(increment).map(increment) == [8,9,7], "dictionary.map() failed"; + } + + //test map with native functions + { + //TODO: write some native functions for use with map + } +} + + //test toLower { assert "Hello World".toLower() == "hello world", "_toLower() failed"; diff --git a/test/scripts/lib/random-stuff.toy b/test/scripts/lib/random-stuff.toy new file mode 100644 index 0000000..0e64750 --- /dev/null +++ b/test/scripts/lib/random-stuff.toy @@ -0,0 +1,40 @@ +//test this logic for memory leaks +{ + import compound; + import timer; + + fn start(k, v) { + return startTimer(); + } + + fn check(k, v) { + var l = v.stopTimer(); + print l.timerToString(); + l.destroyTimer(); + return v; + } + + fn destroy(k, v) { + v.destroyTimer(); + } + + var arr = [1]; + + arr + .map(start) + .map(check) + .map(check) + .map(check) + .map(check) + .map(check) + .map(check) + .map(check) + .map(check) + .map(check) + .map(check) + .map(destroy) + ; +} + + +print "All good"; diff --git a/test/test_libraries.c b/test/test_libraries.c index 9478442..40ac5b6 100644 --- a/test/test_libraries.c +++ b/test/test_libraries.c @@ -53,6 +53,26 @@ void runBinaryWithLibrary(unsigned char* tb, size_t size, char* library, Toy_Hoo Toy_freeInterpreter(&interpreter); } +void runBinaryQuietly(unsigned char* tb, size_t size) { + Toy_Interpreter interpreter; + Toy_initInterpreter(&interpreter); + + //NOTE: supress print output for testing + Toy_setInterpreterPrint(&interpreter, noPrintFn); + Toy_setInterpreterAssert(&interpreter, assertWrapper); + Toy_setInterpreterError(&interpreter, errorWrapper); + + //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); + + Toy_runInterpreter(&interpreter, tb, size); + Toy_freeInterpreter(&interpreter); +} + typedef struct Payload { char* fname; char* libname; @@ -111,6 +131,41 @@ int main() { } } + { + //run whatever, testing stuff together to check for memory leaks + char* whatever[] = { + "random-stuff.toy", + NULL + }; + + for (int i = 0; whatever[i]; i++) { + printf("Running %s\n", whatever[i]); + + char fname[128]; + snprintf(fname, 128, "scripts/lib/%s", whatever[i]); + + //compile the source + size_t size = 0; + char* source = Toy_readFile(fname, &size); + if (!source) { + printf(TOY_CC_ERROR "Failed to load file: %s\n" TOY_CC_RESET, fname); + failedAsserts++; + continue; + } + + unsigned char* tb = Toy_compileString(source, &size); + free((void*)source); + + if (!tb) { + printf(TOY_CC_ERROR "Failed to compile file: %s\n" TOY_CC_RESET, fname); + failedAsserts++; + continue; + } + + runBinaryQuietly(tb, size); + } + } + //lib cleanup Toy_freeDriveDictionary();