From 7b014ebafab140b72dc4ac8a52bfd332e23257cb Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Mon, 11 Apr 2022 16:38:41 -0400 Subject: [PATCH] Fix (reduce) with lambdas. Fix await memory leak. Allow running specific tests with --run-test x (for valgrinding). --- src/env.c | 32 ++++++++++++++++++++++---------- src/env.h | 5 ++--- src/hash.c | 46 +++++++++++++++++++++++++--------------------- src/hash.h | 5 ++++- src/main.c | 10 +++++++++- src/pebblisp.c | 21 +++++++++------------ src/pebblisp.h | 2 -- src/plfunc.c | 21 ++++++++++++++------- src/plfunc.h | 12 ++++++++---- src/tests.sh | 9 ++++++++- src/threads.c | 1 - 11 files changed, 101 insertions(+), 63 deletions(-) diff --git a/src/env.c b/src/env.c index 5391806..b541e44 100644 --- a/src/env.c +++ b/src/env.c @@ -135,6 +135,7 @@ void deleteEnv(struct Environment* e) if (e->refs) { return; } + if (e->outer) { deleteEnv(e->outer); } @@ -225,7 +226,7 @@ struct Environment defaultEnv() unsigned i; for (i = 0; i < sizeof(symFuncs) / sizeof(symFuncs[0]); i++) { - addFunc(symFuncs[i].sym, symFuncs[i].func, &e, i); + addFunc(symFuncs[i].sym, symFuncs[i].func, &e); } helpInitialized = 1; @@ -272,12 +273,10 @@ void printEnv(struct Environment* env, int printPointers) void addFunc(const char* name, Object (* func)(Object*, int, struct Environment*), - struct Environment* env, - int i) + struct Environment* env) { Object o = newObject(TYPE_FUNC); o.func = func; - // addToEnvAt(i, env, name, o); addToEnv(env, name, o); } @@ -427,13 +426,22 @@ Object help(Object* params, int length, struct Environment* env) return nullTerminated("Help not found!"); } -// Returns number of failures -int runTests(int detailed) +// Returns number of failures, or -1 if the requested specificTest is invalid +int runTests(int detailed, int specificTest) { - printf("::NATIVE TESTS::\n"); + int isSpecific = specificTest != -1; + if (specificTest >= currentHelp) { + return -1; + } + + if (!isSpecific) { + printf("::NATIVE TESTS::\n"); + } int failureCount = 0; int passCount = 0; - for (int hi = 0; hi < currentHelp; hi++) { + int start = isSpecific ? specificTest : 0; + int end = isSpecific ? specificTest + 1 : currentHelp; + for (int hi = start; hi < end; hi++) { struct helpText h = helpTexts[hi]; if (h.tests) { if (detailed && h.testCount > 0) { @@ -476,11 +484,15 @@ int runTests(int detailed) if (passCount > 0) { printf(""); } - printf("%d tests passed!\n", passCount); + if (!isSpecific) { + printf("%d tests passed!\n", passCount); + } if (failureCount > 0) { printf(""); } - printf("%d tests failed!\n", failureCount); + if (!isSpecific || failureCount != 0) { + printf("%d tests failed!\n", failureCount); + } // fprintf(stderr, "TOTAL ALLOCATIONS: %d\n", getAllocations()); // fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes()); return failureCount; diff --git a/src/env.h b/src/env.h index f2aaca2..c49425a 100644 --- a/src/env.h +++ b/src/env.h @@ -39,8 +39,7 @@ void printEnv(struct Environment* env, int printPointers); void addFunc(const char* name, Object (* func)(Object*, int, struct Environment*), - struct Environment* env, - int i); + struct Environment* env); void deleteEnv(struct Environment* e); @@ -58,7 +57,7 @@ void addStructDef(struct StructDef def); void printColored(const char* code); -int runTests(int detailed); +int runTests(int detailed, int specificTest); int getTotalSearchDepth(); diff --git a/src/hash.c b/src/hash.c index 51d8ff3..2ea5010 100644 --- a/src/hash.c +++ b/src/hash.c @@ -3,7 +3,7 @@ #include -void addStripped(struct ObjectTable* table, char* name, struct StrippedObject object); +size_t addStripped(struct ObjectTable* table, char* name, struct StrippedObject object); Object deStrip(struct StrippedObject object) { @@ -43,30 +43,32 @@ void deleteTable(struct ObjectTable* table) #ifndef HASHLESS_ENV -static unsigned long hash(const char* str, struct ObjectTable* table) +static size_t hash(const char* str, struct ObjectTable* table) { - unsigned long hash = 5381; - int c; + size_t hash = 5381; + char c; while ((c = *str++)) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } - return hash % table->capacity; + return hash; } void extendTable(struct ObjectTable* table) { - if (table->capacity < (table->count + 1) * 2) { - struct ObjectTable newTable = buildTable(table->capacity ? table->capacity * 2 : 4); - for (int i = 0; i < table->capacity; i++) { - if (table->elements[i].symbol) { - addStripped(&newTable, table->elements[i].symbol, table->elements[i].object); - } - } - free(table->elements); - *table = newTable; + if (table->capacity >= (table->count + 1) * 2) { + return; } + + struct ObjectTable newTable = buildTable(table->capacity ? table->capacity * 2 : 4); + for (int i = 0; i < table->capacity; i++) { + if (table->elements[i].symbol) { + addStripped(&newTable, table->elements[i].symbol, table->elements[i].object); + } + } + free(table->elements); + *table = newTable; } struct StrippedObject* getFromTable(struct ObjectTable* table, const char* name) @@ -74,7 +76,7 @@ struct StrippedObject* getFromTable(struct ObjectTable* table, const char* name) if (table->capacity == 0) { return NULL; } - size_t h = hash(name, table); + size_t h = hash(name, table) % table->capacity; while (table->elements[h].symbol) { if (strcmp(name, table->elements[h].symbol) == 0) { return &table->elements[h].object; @@ -103,7 +105,7 @@ void extendTable(struct ObjectTable* table) } } -Object* getFromTable(struct ObjectTable* table, const char* name) +struct StrippedObject* getFromTable(struct ObjectTable* table, const char* name) { for (size_t i = 0; i < table->count; i++) { if (strcmp(name, table->elements[i].symbol) == 0) { @@ -113,32 +115,34 @@ Object* getFromTable(struct ObjectTable* table, const char* name) return NULL; } -static unsigned long hash(unused const char* str, struct ObjectTable* table) +static size_t hash(unused const char* str, struct ObjectTable* table) { return table->count; } #endif -void addStripped(struct ObjectTable* table, char* name, struct StrippedObject object) +size_t addStripped(struct ObjectTable* table, char* name, struct StrippedObject object) { extendTable(table); - size_t h = hash(name, table); + size_t initial = hash(name, table); + size_t h = initial % table->capacity; while (table->elements[h].symbol) { h = (h + 1) % table->capacity; } table->elements[h].symbol = name; table->elements[h].object = object; table->count += 1; + return initial; } /// /// \param table /// \param name Should be a new allocation /// \param object Should already be cloned -void addToTable(struct ObjectTable* table, char* name, Object object) +size_t addToTable(struct ObjectTable* table, char* name, Object object) { - addStripped(table, name, (struct StrippedObject) { + return addStripped(table, name, (struct StrippedObject) { .data = object.data, .type = object.type, }); diff --git a/src/hash.h b/src/hash.h index ca2f084..0331227 100644 --- a/src/hash.h +++ b/src/hash.h @@ -11,7 +11,10 @@ struct ObjectTable { struct ObjectTable buildTable(size_t capacity); -void addToTable(struct ObjectTable* table, char* name, Object object); +/// Returns the hash of the inserted object +size_t addToTable(struct ObjectTable* table, char* name, Object object); + +struct StrippedObject* getWithHash(struct ObjectTable* table, size_t hash); struct StrippedObject* getFromTable(struct ObjectTable* table, const char* name); diff --git a/src/main.c b/src/main.c index 60aca9f..c77abb6 100644 --- a/src/main.c +++ b/src/main.c @@ -10,6 +10,7 @@ struct Settings { int runTests; + long specificTest; int ignoreConfig; int ignoreLib; int moreToDo; @@ -183,6 +184,7 @@ void setupSegfaultHandler() } #endif +#define SPECIFIC_TEST_ARG "--run-test" #define RUN_TESTS_ARG "--run-tests" #define RUN_DETAILED_TESTS "=detailed" #define IGNORE_CONFIG_ARG "--ignore-config" @@ -194,6 +196,7 @@ void getSettings(int argc, const char* argv[]) const char* const home = getenv("HOME"); settings.runTests = 0; + settings.specificTest = -1; settings.ignoreConfig = 0; settings.ignoreLib = 0; settings.moreToDo = 0; @@ -203,12 +206,17 @@ void getSettings(int argc, const char* argv[]) size_t runTestsLen = strlen(RUN_TESTS_ARG); size_t configFileLen = strlen(CONFIG_FILE_ARG); + size_t specificTestLen = strlen(SPECIFIC_TEST_ARG); for (int i = 1; i < argc; i++) { if (strncmp(argv[i], RUN_TESTS_ARG, runTestsLen) == 0) { int isDetailed = strcmp(argv[i] + runTestsLen, RUN_DETAILED_TESTS) == 0; settings.runTests = isDetailed ? 2 : 1; } else if (strncmp(argv[i], CONFIG_FILE_ARG, configFileLen) == 0) { settings.configFile = argv[i] + configFileLen; + } else if (strncmp(argv[i], SPECIFIC_TEST_ARG, specificTestLen) == 0) { + settings.runTests = 2; + char* invalid; + settings.specificTest = strtol(argv[i + 1], &invalid, 10); } else if (strcmp(argv[i], IGNORE_CONFIG_ARG) == 0) { settings.ignoreConfig = 1; } else if (strcmp(argv[i], IGNORE_LIB_ARG) == 0) { @@ -230,7 +238,7 @@ int main(int argc, const char* argv[]) setGlobal(&env); if (settings.runTests) { - int ret = runTests(settings.runTests == 2); + int ret = runTests(settings.runTests == 2, settings.specificTest); shredDictionary(); deleteEnv(global()); return ret; diff --git a/src/pebblisp.c b/src/pebblisp.c index 9e09e79..8831201 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -141,17 +141,6 @@ Object listEvalFunc(const Object* function, const Object* paramList, return result; } -Object simpleFuncEval(const Object func, Object arg1, Object arg2, struct Environment* env) -{ - arg2 = cloneObject(arg2); - arg1.forward = &arg2; - Object first_eval = eval(&func, env); - Object ret = funcyEval(&first_eval, &arg1, 2, env); - cleanObject(&first_eval); - cleanObject(&arg2); - return ret; -} - /** * Evaluates a list whose first element is a lambda, applying that lambda * @@ -180,6 +169,14 @@ Object listEvalLambda(Object* lambda, const Object* passedArguments, int evalLen return ret; } +/** + * Run a func-like object with the given parameters + * @param funcy The func-like. Should be a fresh object! Will be freed! + * @param passedArguments Ongoing (forward->forward) list of arguments. + * @param evalLength Number of parameters to the func-like object. + * @param env + * @return The result from the func-like object. + */ Object funcyEval(Object* funcy, const Object* passedArguments, int evalLength, struct Environment* env) { @@ -300,7 +297,7 @@ Object eval(const Object* obj, struct Environment* env) return evalList(obj, env); } - return errorObject(BAD_TYPE); + return errorWithContext(BAD_TYPE, "eval()"); } Object structAccess(Object* params, unused int length, unused struct Environment* env) diff --git a/src/pebblisp.h b/src/pebblisp.h index 9eb75e6..cbe7432 100644 --- a/src/pebblisp.h +++ b/src/pebblisp.h @@ -83,8 +83,6 @@ Object listEvalLambda(Object* lambda, const Object* passedArguments, int evalLen Object funcyEval(Object* funcy, const Object* passedArguments, int evalLength, struct Environment* env); -Object simpleFuncEval(Object func, Object arg1, Object arg2, struct Environment* env); - Object typeCheck(const char* funcName, Object* params, int length, struct TypeCheck typeChecks[], int typeLength, int* failed); diff --git a/src/plfunc.c b/src/plfunc.c index a145e10..48a5988 100644 --- a/src/plfunc.c +++ b/src/plfunc.c @@ -8,21 +8,24 @@ Object reduce(Object* params, unused int length, struct Environment* env) { checkTypes(reduce) Object list = params[0]; - const Object func = params[1]; + Object func = params[1]; Object total = params[2]; if (list.type != TYPE_LIST) { - return simpleFuncEval(func, total, list, env); + func = cloneObject(func); + list.forward = &total; + return funcyEval(&func, &list, 2, env); } - int isFirst = 1; + Object* first = list.list; FOR_POINTER_IN_LIST(&list) { + func = cloneObject(func); Object oldTotal = total; - total = simpleFuncEval(func, total, *POINTER, env); - if (!isFirst) { + total.forward = POINTER; + total = funcyEval(&func, &total, 2, env); + if (POINTER != first) { cleanObject(&oldTotal); } - isFirst = 0; } return total; @@ -560,7 +563,11 @@ Object readFileToObject(Object* params, unused int length, unused struct Environ Object getEnvVar(Object* params, unused int length, unused struct Environment* env) { checkTypes(getEnvVar) - return nullTerminated(getenv(params[0].string)); + const char* envVar = getenv(params[0].string); + if (envVar) { + return nullTerminated(envVar); + } + return stringFromSlice("", 0); } #endif // STANDALONE diff --git a/src/plfunc.h b/src/plfunc.h index a797e9e..0c78bbd 100644 --- a/src/plfunc.h +++ b/src/plfunc.h @@ -102,13 +102,15 @@ tfn(len, "len", tfn(reduce, "reduce", ({ anyType, expect(isFuncy), anyType, anyType }), - "Performs a simple reduction. Does not currently work with lambdas.\n" // TODO: Still not working + "Performs a simple reduction.\n" "Takes three arguments:\n" " - Values\n" " - A function to apply to each value\n" " - An initial value", "(reduce 5 + 6)", "11", "(reduce (1 2 3) + 0)", "6", + "(def sum (fn (left right) ((+ left right)) )) (reduce 5 sum 6)", "11", + "(def sum (fn (left right) ((+ left right)) )) (reduce (1 2 3) sum 0)", "6", ); tfn(at, "at", @@ -263,12 +265,14 @@ tfn(systemCall, "sys", tfn(cd, "cd", ({ expect(isStringy), anyType }), - "Change the current directory." + "Change the current directory.", + "(cd \"/\") (cwd)", "/" ); tfn(cwd, "cwd", ({ returns(isStringy) }), - "Get the current directory." + "Get the current directory.", + "(cd \"/\") (cwd)", "/" ); /// @code @@ -277,7 +281,7 @@ tfn(cwd, "cwd", fn(takeInput, "inp", "Take console input with an optional prompt. For example:\n" "`(def x (input))` will wait for user input with no prompt.\n" - "`(def x (input \">> \"))` wait for input, but prompt the user with '>> '.\n" + "`(def x (input \">> \"))` wait for input, but prompt the user with '>> '\n" ); tfn(readFileToObject, "rf", diff --git a/src/tests.sh b/src/tests.sh index d3513b5..f7dc918 100755 --- a/src/tests.sh +++ b/src/tests.sh @@ -274,7 +274,14 @@ echo "$TOTAL_FAILS Tests Failed" echo "" if $VALGRIND; then - $VALCOM ./pl --run-tests=detailed + i=0 + while true; do + valgrind -q --leak-check=full --error-exitcode=1 --track-origins=yes ./pl --run-test $i + if [[ "$?" == "255" ]]; then + break + fi + i=$((i+1)) + done else ./pl --run-tests fi diff --git a/src/threads.c b/src/threads.c index 3607e76..4a980bd 100644 --- a/src/threads.c +++ b/src/threads.c @@ -75,7 +75,6 @@ Object async(Object* params, int length, struct Environment* env) .env = env, .object = cloneObject(params[0]), }; - env->refs += 1; pthread_create(&promise.promise->thread, NULL, doAsync, promise.promise); return promise;