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) {
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)
{
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("");
}
if (!isSpecific) {
printf("%d tests passed!\n", passCount);
}
if (failureCount > 0) {
printf("");
}
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;

View File

@ -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();

View File

@ -3,7 +3,7 @@
#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)
{
@ -43,21 +43,24 @@ 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) {
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) {
@ -67,14 +70,13 @@ void extendTable(struct ObjectTable* table)
free(table->elements);
*table = newTable;
}
}
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,
});

View File

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

View File

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

View File

@ -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)

View File

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

View File

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

View File

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

View File

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

View File

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