Added _forEach and _map, added tests for them

This commit is contained in:
2023-02-06 00:51:07 +00:00
parent c0ec5ef28e
commit c875ae7a0e
6 changed files with 358 additions and 15 deletions

View File

@@ -115,6 +115,81 @@ static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* argument
return -1; 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) { static int nativeGetKeys(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) { if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getKeys\n"); interpreter->errorOutput("Incorrect number of arguments to _getKeys\n");
@@ -205,6 +280,105 @@ static int nativeGetValues(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
return 1; 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) { static int nativeToLower(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments //no arguments
if (arguments->count != 1) { if (arguments->count != 1) {
@@ -477,11 +651,12 @@ int Toy_hookCompound(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_L
// {"_containsValue", native}, //array, dictionary // {"_containsValue", native}, //array, dictionary
// {"_every", native}, //array, dictionary, string // {"_every", native}, //array, dictionary, string
// {"_filter", native}, //array, dictionary // {"_filter", native}, //array, dictionary
{"_forEach", nativeForEach}, //array, dictionary
{"_getKeys", nativeGetKeys}, //dictionary {"_getKeys", nativeGetKeys}, //dictionary
{"_getValues", nativeGetValues}, //dictionary {"_getValues", nativeGetValues}, //dictionary
// {"_indexOf", native}, //array, string // {"_indexOf", native}, //array, string
// {"_insert", native}, //array, dictionary, string // {"_insert", native}, //array, dictionary, string
// {"_map", native}, //array, dictionary {"_map", nativeMap}, //array, dictionary
// {"_reduce", native}, //array, dictionary // {"_reduce", native}, //array, dictionary
// {"_remove", native}, //array, dictionary // {"_remove", native}, //array, dictionary
// {"_replace", native}, //string // {"_replace", native}, //string

View File

@@ -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";

View File

@@ -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) { 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)) { if (!TOY_IS_FUNCTION(func)) {
interpreter->errorOutput("Function required in Toy_callLiteralFn()\n"); interpreter->errorOutput("Function required in Toy_callLiteralFn()\n");
return false; return false;

View File

@@ -1,5 +1,6 @@
import compound; import compound;
//test concat
{ {
//test array 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 //test getKeys
{ {
var d = ["foo": 1, "bar": 2]; 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 //test toLower
{ {
assert "Hello World".toLower() == "hello world", "_toLower() failed"; assert "Hello World".toLower() == "hello world", "_toLower() failed";

View File

@@ -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";

View File

@@ -53,6 +53,26 @@ void runBinaryWithLibrary(unsigned char* tb, size_t size, char* library, Toy_Hoo
Toy_freeInterpreter(&interpreter); 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 { typedef struct Payload {
char* fname; char* fname;
char* libname; 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 //lib cleanup
Toy_freeDriveDictionary(); Toy_freeDriveDictionary();