Fix (reduce) with lambdas.

Fix await memory leak.
Allow running specific tests with --run-test x (for valgrinding).
This commit is contained in:
Sage Vaillancourt 2022-04-11 16:38:41 -04:00 committed by Sage Vaillancourt
parent 5a34bcbfbd
commit 7b014ebafa
11 changed files with 101 additions and 63 deletions

View File

@ -135,6 +135,7 @@ void deleteEnv(struct Environment* e)
if (e->refs) { if (e->refs) {
return; return;
} }
if (e->outer) { if (e->outer) {
deleteEnv(e->outer); deleteEnv(e->outer);
} }
@ -225,7 +226,7 @@ struct Environment defaultEnv()
unsigned i; unsigned i;
for (i = 0; i < sizeof(symFuncs) / sizeof(symFuncs[0]); 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; helpInitialized = 1;
@ -272,12 +273,10 @@ void printEnv(struct Environment* env, int printPointers)
void addFunc(const char* name, void addFunc(const char* name,
Object (* func)(Object*, int, struct Environment*), Object (* func)(Object*, int, struct Environment*),
struct Environment* env, struct Environment* env)
int i)
{ {
Object o = newObject(TYPE_FUNC); Object o = newObject(TYPE_FUNC);
o.func = func; o.func = func;
// addToEnvAt(i, env, name, o);
addToEnv(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!"); return nullTerminated("Help not found!");
} }
// Returns number of failures // Returns number of failures, or -1 if the requested specificTest is invalid
int runTests(int detailed) int runTests(int detailed, int specificTest)
{ {
int isSpecific = specificTest != -1;
if (specificTest >= currentHelp) {
return -1;
}
if (!isSpecific) {
printf("::NATIVE TESTS::\n"); printf("::NATIVE TESTS::\n");
}
int failureCount = 0; int failureCount = 0;
int passCount = 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]; struct helpText h = helpTexts[hi];
if (h.tests) { if (h.tests) {
if (detailed && h.testCount > 0) { if (detailed && h.testCount > 0) {
@ -476,11 +484,15 @@ int runTests(int detailed)
if (passCount > 0) { if (passCount > 0) {
printf(""); printf("");
} }
if (!isSpecific) {
printf("%d tests passed!\n", passCount); printf("%d tests passed!\n", passCount);
}
if (failureCount > 0) { if (failureCount > 0) {
printf(""); printf("");
} }
if (!isSpecific || failureCount != 0) {
printf("%d tests failed!\n", failureCount); printf("%d tests failed!\n", failureCount);
}
// fprintf(stderr, "TOTAL ALLOCATIONS: %d\n", getAllocations()); // fprintf(stderr, "TOTAL ALLOCATIONS: %d\n", getAllocations());
// fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes()); // fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes());
return failureCount; return failureCount;

View File

@ -39,8 +39,7 @@ void printEnv(struct Environment* env, int printPointers);
void addFunc(const char* name, void addFunc(const char* name,
Object (* func)(Object*, int, struct Environment*), Object (* func)(Object*, int, struct Environment*),
struct Environment* env, struct Environment* env);
int i);
void deleteEnv(struct Environment* e); void deleteEnv(struct Environment* e);
@ -58,7 +57,7 @@ void addStructDef(struct StructDef def);
void printColored(const char* code); void printColored(const char* code);
int runTests(int detailed); int runTests(int detailed, int specificTest);
int getTotalSearchDepth(); int getTotalSearchDepth();

View File

@ -3,7 +3,7 @@
#include <string.h> #include <string.h>
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) Object deStrip(struct StrippedObject object)
{ {
@ -43,21 +43,24 @@ void deleteTable(struct ObjectTable* table)
#ifndef HASHLESS_ENV #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; size_t hash = 5381;
int c; char c;
while ((c = *str++)) { while ((c = *str++)) {
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
} }
return hash % table->capacity; return hash;
} }
void extendTable(struct ObjectTable* table) void extendTable(struct ObjectTable* table)
{ {
if (table->capacity < (table->count + 1) * 2) { if (table->capacity >= (table->count + 1) * 2) {
return;
}
struct ObjectTable newTable = buildTable(table->capacity ? table->capacity * 2 : 4); struct ObjectTable newTable = buildTable(table->capacity ? table->capacity * 2 : 4);
for (int i = 0; i < table->capacity; i++) { for (int i = 0; i < table->capacity; i++) {
if (table->elements[i].symbol) { if (table->elements[i].symbol) {
@ -67,14 +70,13 @@ void extendTable(struct ObjectTable* table)
free(table->elements); free(table->elements);
*table = newTable; *table = newTable;
} }
}
struct StrippedObject* getFromTable(struct ObjectTable* table, const char* name) struct StrippedObject* getFromTable(struct ObjectTable* table, const char* name)
{ {
if (table->capacity == 0) { if (table->capacity == 0) {
return NULL; return NULL;
} }
size_t h = hash(name, table); size_t h = hash(name, table) % table->capacity;
while (table->elements[h].symbol) { while (table->elements[h].symbol) {
if (strcmp(name, table->elements[h].symbol) == 0) { if (strcmp(name, table->elements[h].symbol) == 0) {
return &table->elements[h].object; 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++) { for (size_t i = 0; i < table->count; i++) {
if (strcmp(name, table->elements[i].symbol) == 0) { if (strcmp(name, table->elements[i].symbol) == 0) {
@ -113,32 +115,34 @@ Object* getFromTable(struct ObjectTable* table, const char* name)
return NULL; 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; return table->count;
} }
#endif #endif
void addStripped(struct ObjectTable* table, char* name, struct StrippedObject object) size_t addStripped(struct ObjectTable* table, char* name, struct StrippedObject object)
{ {
extendTable(table); 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) { while (table->elements[h].symbol) {
h = (h + 1) % table->capacity; h = (h + 1) % table->capacity;
} }
table->elements[h].symbol = name; table->elements[h].symbol = name;
table->elements[h].object = object; table->elements[h].object = object;
table->count += 1; table->count += 1;
return initial;
} }
/// ///
/// \param table /// \param table
/// \param name Should be a new allocation /// \param name Should be a new allocation
/// \param object Should already be cloned /// \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, .data = object.data,
.type = object.type, .type = object.type,
}); });

View File

@ -11,7 +11,10 @@ struct ObjectTable {
struct ObjectTable buildTable(size_t capacity); 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); struct StrippedObject* getFromTable(struct ObjectTable* table, const char* name);

View File

@ -10,6 +10,7 @@
struct Settings { struct Settings {
int runTests; int runTests;
long specificTest;
int ignoreConfig; int ignoreConfig;
int ignoreLib; int ignoreLib;
int moreToDo; int moreToDo;
@ -183,6 +184,7 @@ void setupSegfaultHandler()
} }
#endif #endif
#define SPECIFIC_TEST_ARG "--run-test"
#define RUN_TESTS_ARG "--run-tests" #define RUN_TESTS_ARG "--run-tests"
#define RUN_DETAILED_TESTS "=detailed" #define RUN_DETAILED_TESTS "=detailed"
#define IGNORE_CONFIG_ARG "--ignore-config" #define IGNORE_CONFIG_ARG "--ignore-config"
@ -194,6 +196,7 @@ void getSettings(int argc, const char* argv[])
const char* const home = getenv("HOME"); const char* const home = getenv("HOME");
settings.runTests = 0; settings.runTests = 0;
settings.specificTest = -1;
settings.ignoreConfig = 0; settings.ignoreConfig = 0;
settings.ignoreLib = 0; settings.ignoreLib = 0;
settings.moreToDo = 0; settings.moreToDo = 0;
@ -203,12 +206,17 @@ void getSettings(int argc, const char* argv[])
size_t runTestsLen = strlen(RUN_TESTS_ARG); size_t runTestsLen = strlen(RUN_TESTS_ARG);
size_t configFileLen = strlen(CONFIG_FILE_ARG); size_t configFileLen = strlen(CONFIG_FILE_ARG);
size_t specificTestLen = strlen(SPECIFIC_TEST_ARG);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strncmp(argv[i], RUN_TESTS_ARG, runTestsLen) == 0) { if (strncmp(argv[i], RUN_TESTS_ARG, runTestsLen) == 0) {
int isDetailed = strcmp(argv[i] + runTestsLen, RUN_DETAILED_TESTS) == 0; int isDetailed = strcmp(argv[i] + runTestsLen, RUN_DETAILED_TESTS) == 0;
settings.runTests = isDetailed ? 2 : 1; settings.runTests = isDetailed ? 2 : 1;
} else if (strncmp(argv[i], CONFIG_FILE_ARG, configFileLen) == 0) { } else if (strncmp(argv[i], CONFIG_FILE_ARG, configFileLen) == 0) {
settings.configFile = argv[i] + configFileLen; 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) { } else if (strcmp(argv[i], IGNORE_CONFIG_ARG) == 0) {
settings.ignoreConfig = 1; settings.ignoreConfig = 1;
} else if (strcmp(argv[i], IGNORE_LIB_ARG) == 0) { } else if (strcmp(argv[i], IGNORE_LIB_ARG) == 0) {
@ -230,7 +238,7 @@ int main(int argc, const char* argv[])
setGlobal(&env); setGlobal(&env);
if (settings.runTests) { if (settings.runTests) {
int ret = runTests(settings.runTests == 2); int ret = runTests(settings.runTests == 2, settings.specificTest);
shredDictionary(); shredDictionary();
deleteEnv(global()); deleteEnv(global());
return ret; return ret;

View File

@ -141,17 +141,6 @@ Object listEvalFunc(const Object* function, const Object* paramList,
return result; 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 * 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; 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, Object funcyEval(Object* funcy, const Object* passedArguments, int evalLength,
struct Environment* env) struct Environment* env)
{ {
@ -300,7 +297,7 @@ Object eval(const Object* obj, struct Environment* env)
return evalList(obj, 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) Object structAccess(Object* params, unused int length, unused struct Environment* env)

View File

@ -83,8 +83,6 @@ Object listEvalLambda(Object* lambda, const Object* passedArguments, int evalLen
Object funcyEval(Object* funcy, const Object* passedArguments, int evalLength, Object funcyEval(Object* funcy, const Object* passedArguments, int evalLength,
struct Environment* env); struct Environment* env);
Object simpleFuncEval(Object func, Object arg1, Object arg2, struct Environment* env);
Object typeCheck(const char* funcName, Object* params, int length, Object typeCheck(const char* funcName, Object* params, int length,
struct TypeCheck typeChecks[], int typeLength, int* failed); struct TypeCheck typeChecks[], int typeLength, int* failed);

View File

@ -8,21 +8,24 @@ Object reduce(Object* params, unused int length, struct Environment* env)
{ {
checkTypes(reduce) checkTypes(reduce)
Object list = params[0]; Object list = params[0];
const Object func = params[1]; Object func = params[1];
Object total = params[2]; Object total = params[2];
if (list.type != TYPE_LIST) { 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) { FOR_POINTER_IN_LIST(&list) {
func = cloneObject(func);
Object oldTotal = total; Object oldTotal = total;
total = simpleFuncEval(func, total, *POINTER, env); total.forward = POINTER;
if (!isFirst) { total = funcyEval(&func, &total, 2, env);
if (POINTER != first) {
cleanObject(&oldTotal); cleanObject(&oldTotal);
} }
isFirst = 0;
} }
return total; 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) Object getEnvVar(Object* params, unused int length, unused struct Environment* env)
{ {
checkTypes(getEnvVar) 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 #endif // STANDALONE

View File

@ -102,13 +102,15 @@ tfn(len, "len",
tfn(reduce, "reduce", tfn(reduce, "reduce",
({ anyType, expect(isFuncy), anyType, anyType }), ({ 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" "Takes three arguments:\n"
" - Values\n" " - Values\n"
" - A function to apply to each value\n" " - A function to apply to each value\n"
" - An initial value", " - An initial value",
"(reduce 5 + 6)", "11", "(reduce 5 + 6)", "11",
"(reduce (1 2 3) + 0)", "6", "(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", tfn(at, "at",
@ -263,12 +265,14 @@ tfn(systemCall, "sys",
tfn(cd, "cd", tfn(cd, "cd",
({ expect(isStringy), anyType }), ({ expect(isStringy), anyType }),
"Change the current directory." "Change the current directory.",
"(cd \"/\") (cwd)", "/"
); );
tfn(cwd, "cwd", tfn(cwd, "cwd",
({ returns(isStringy) }), ({ returns(isStringy) }),
"Get the current directory." "Get the current directory.",
"(cd \"/\") (cwd)", "/"
); );
/// @code /// @code
@ -277,7 +281,7 @@ tfn(cwd, "cwd",
fn(takeInput, "inp", fn(takeInput, "inp",
"Take console input with an optional prompt. For example:\n" "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))` 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", tfn(readFileToObject, "rf",

View File

@ -274,7 +274,14 @@ echo "$TOTAL_FAILS Tests Failed"
echo "" echo ""
if $VALGRIND; then 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 else
./pl --run-tests ./pl --run-tests
fi fi

View File

@ -75,7 +75,6 @@ Object async(Object* params, int length, struct Environment* env)
.env = env, .env = env,
.object = cloneObject(params[0]), .object = cloneObject(params[0]),
}; };
env->refs += 1;
pthread_create(&promise.promise->thread, NULL, doAsync, promise.promise); pthread_create(&promise.promise->thread, NULL, doAsync, promise.promise);
return promise; return promise;