Fix (reduce) with lambdas.
Fix await memory leak. Allow running specific tests with --run-test x (for valgrinding).
This commit is contained in:
parent
5a34bcbfbd
commit
7b014ebafa
26
src/env.c
26
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)
|
||||
{
|
||||
int isSpecific = specificTest != -1;
|
||||
if (specificTest >= currentHelp) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!isSpecific) {
|
||||
printf("[35;1m::NATIVE TESTS::[0m\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("[32;1m");
|
||||
}
|
||||
if (!isSpecific) {
|
||||
printf("%d tests passed![0m\n", passCount);
|
||||
}
|
||||
if (failureCount > 0) {
|
||||
printf("[31m");
|
||||
}
|
||||
if (!isSpecific || failureCount != 0) {
|
||||
printf("%d tests failed![0m\n", failureCount);
|
||||
}
|
||||
// fprintf(stderr, "TOTAL ALLOCATIONS: %d\n", getAllocations());
|
||||
// fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes());
|
||||
return failureCount;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
32
src/hash.c
32
src/hash.c
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
10
src/main.c
10
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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
21
src/plfunc.c
21
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
|
||||
|
|
12
src/plfunc.h
12
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",
|
||||
|
|
|
@ -274,7 +274,14 @@ echo "$TOTAL_FAILS Tests Failed[0m"
|
|||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue