#include "env.h" #include #include #include #include "pebblisp.h" #include "web.h" #include "plfunc.h" #define printd(...) ; struct Environment* globalEnv; struct Environment* global() { return globalEnv; } void setGlobal(struct Environment* env) { globalEnv = env; } struct Dictionary { int structCount; int structCapacity; struct StructDef* structDefs; } dictionary; Object fetchFromEnvironment(const char* name, struct Environment* env) { if (!env) { return errorObject(NULL_ENV); } if (env->capacity == 0) { if (env->outer) { return fetchFromEnvironment(name, env->outer); } else { return errorObject(EMPTY_ENV); } } for (int i = 0; i < env->capacity; i++) { if (env->strings[i] == NULL) { printd("Try %d (NULL)\n", i); break; } printd("Try %d (%s)\n", i, env->strings[i]); if (strcmp(name, env->strings[i]) == 0) { printd("Returning!\n"); return cloneObject(env->objects[i]); } } printd("Trying outer %p\n", env->outer); if (env->outer) { return fetchFromEnvironment(name, env->outer); } return errorWithContext(DID_NOT_FIND_SYMBOL, name); } void addToEnvAt(int i, struct Environment* env, const char* name, const Object obj) { env->strings[i] = malloc(sizeof(char) * strlen(name) + 1); strcpy(env->strings[i], name); env->objects[i] = cloneObject(obj); } struct Environment envForLambda(const Object* params, const Object* arg_forms, int paramCount, struct Environment* outer) { if (outer) { outer->refs += 1; } struct Environment env = { .outer = outer, .capacity = paramCount, .refs = 1, }; if (paramCount == 0) { if (outer) { outer->refs += 1; return *outer; } env.strings = NULL; env.objects = NULL; return env; } env.strings = malloc(sizeof(char*) * (paramCount + 1)); env.objects = malloc(sizeof(Object) * (paramCount + 1)); const Object* march = arg_forms; for (int i = 0; i < paramCount; i++) { const char* newObjName = itemAt(params, i)->string; // Eval the `march` list Object newEnvObj = march ? eval(march, outer) : errorWithContext(NOT_ENOUGH_ARGUMENTS, newObjName); addToEnvAt(i, &env, newObjName, newEnvObj); // Could use eval_forms? cleanObject(&newEnvObj); march = march ? march->forward : NULL; } env.strings[paramCount] = NULL; return env; } void addToEnv(struct Environment* env, const char* name, const Object obj) { struct Environment* temp_env = env; while (temp_env) { for (int i = 0; i < temp_env->capacity; i++) { if (temp_env->strings[i] == NULL) { // Add *new* item to env only if we're in the original scope, // otherwise keep searching if (temp_env == env) { addToEnvAt(i, env, name, obj); return; } break; } else if (strcmp(temp_env->strings[i], name) == 0) { Object o = cloneObject(obj); cleanObject(&temp_env->objects[i]); temp_env->objects[i] = o; return; } } temp_env = temp_env->outer; } printd("Reallocating environment\n"); const int inc = 5; const int old_size = env->capacity; env->capacity += inc; env->strings = realloc(env->strings, sizeof(char*) * env->capacity); env->objects = realloc(env->objects, sizeof(Object) * env->capacity); for (int j = 0; j < inc; j++) { env->strings[old_size + j] = NULL; } addToEnvAt(old_size, env, name, obj); } #ifdef STANDALONE void printEnv(struct Environment* env, int printPointers) { if (!env) { printf("NULL env\n"); return; } printf("env->capacity = %d\n", env->capacity); for (int i = 0; i < env->capacity; i++) { printf(""); if (env->strings[i] == NULL) { printf("[%d]: NULL - End of Environment\n", i); break; } if (printPointers) { printf("[%d]: `%s` %p :: ", i, env->strings[i], env->strings[i]); } else { printf("[%d]: `%s` :: ", i, env->strings[i]); } printf(""); if (env->objects[i].type == TYPE_STRING) { printf("\""); } if (env->objects[i].type == TYPE_FUNC && !printPointers) { printf("Native"); } else { size_t length; char* s = stringObj(&env->objects[i], &length); printColored(s); if (env->objects[i].type == TYPE_STRING) { printf("\""); } free(s); } printf("\n"); } } #endif void addFunc(const char* name, Object (* func)(Object*, int, struct Environment*), struct Environment* env, int i) { Object o = newObject(TYPE_FUNC); o.func = func; addToEnvAt(i, env, name, o); } void deleteEnv(struct Environment* e) { e->refs -= 1; if (e->refs) { return; } if (e->outer) { deleteEnv(e->outer); } if (e->strings) { int i = 0; while (e->strings[i]) { free(e->strings[i]); cleanObject(&e->objects[i]); i++; } free(e->strings); free(e->objects); } } void shredDictionary() { for (int i = 0; i < dictionary.structCount; i++) { free(dictionary.structDefs[i].name); for (int j = 0; j < dictionary.structDefs[i].fieldCount; j++) { free(dictionary.structDefs[i].names[j]); } free(dictionary.structDefs[i].names); } free(dictionary.structDefs); } struct symFunc { const char* sym; Object (* func)(Object*, int, struct Environment*); }; #ifdef STANDALONE struct helpText { const char* symbol; const char* help; const char* const* tests; size_t testCount; }; int currentHelp = 0; struct helpText helpTexts[100]; /// For any instances (e.g. segfault recovery) where defaultEnv() may be called /// multiple times. int helpInitialized = 0; #endif #ifdef STANDALONE struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help, const char* const* tests, size_t testLength) { if (!helpInitialized) { helpTexts[currentHelp].help = help; helpTexts[currentHelp].symbol = symbol; helpTexts[currentHelp].tests = tests; helpTexts[currentHelp].testCount = testLength; currentHelp += 1; } return (struct symFunc) { .func = func, .sym = symbol, }; } #else struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*)) { return (struct symFunc) { .func = func, .sym = symbol, }; } #endif #ifdef STANDALONE const int black = 30; const int red = 31; const int green = 32; const int yellow = 33; const int cyan = 34; const int purple = 35; const int teal = 36; const int white = 37; const int colors[] = { white, green, cyan, yellow, purple, red }; const int colorCount = array_length(colors); int getColor(int depth) { depth = depth >= 0 ? depth : depth * -1; return colors[depth % colorCount]; } void printColored(const char* code) { int c = 0; int depth = 0; int isQuote = 0; printf("[%dm", getColor(depth)); while (code[c]) { if (code[c] == '(' && !isQuote) { depth += 1; printf("[%dm", getColor(depth)); } else if (code[c] == ')' && !isQuote) { depth -= 1; printf(")[%dm", getColor(depth)); c++; continue; } else if (code[c] == '"') { isQuote = !isQuote; if (isQuote) { printf("[%dm\"", teal); } else { printf("\"[%dm", getColor(depth)); } c++; continue; } else if (code[c] == '=' && code[c + 1] == '>' && !isQuote) { printf("=>[0;%dm", getColor(depth)); c += 2; continue; } printf("%c", code[c]); c++; } } fn(help, "?", "Gets a string with help text for the given function.\n" "For example:\n" " (? islist) => \"(islist (1 2 3)) => T\"" ) { const char* symbol; if (!length || !params[0].string || params[0].string[0] == '\0') { symbol = "?"; } else { symbol = params[0].string; } for (int i = 0; i < currentHelp; i++) { struct helpText h = helpTexts[i]; if (strcmp(symbol, h.symbol) == 0) { Object text = withLen(1024, TYPE_STRING); char* textCursor = text.string; textCursor += sprintf(textCursor, "%s", h.help); for (int ti = 0; ti < h.testCount; ti += 2) { const char* test = h.tests[ti]; const char* expected = h.tests[ti + 1]; textCursor += sprintf(textCursor, "\n %s => ", test); if (expected[0] == '\0') { textCursor += sprintf(textCursor, ""); } else { textCursor += sprintf(textCursor, "%s", expected); } } return text; } } return nullTerminated("Help not found!"); } // Returns number of failures int runTests(int detailed) { printf("::NATIVE TESTS::\n"); int failureCount = 0; int passCount = 0; for (int hi = 0; hi < currentHelp; hi++) { struct helpText h = helpTexts[hi]; if (h.tests) { if (detailed && h.testCount > 0) { printf(" `%s` ", h.symbol); } for (int ti = 0; ti < h.testCount; ti += 2) { const char* test = h.tests[ti]; const char* expected = h.tests[ti + 1]; struct Environment env = defaultEnv(); Object o = parseEval(test, &env); size_t length; char* result = stringObj(&o, &length); cleanObject(&o); if (strcmp(result, expected) != 0) { failureCount++; printf("Test failed!\n"); printf("%s\n", test); printf("Expected '%s' but received '%s'\n", expected, result); } else { if (detailed) { printf("✓"); } passCount++; } free(result); deleteEnv(&env); } if (detailed && h.testCount > 0) { printf("\n"); } } } if (passCount > 0) { printf(""); } printf("%d tests passed!\n", passCount); if (failureCount > 0) { printf(""); } printf("%d tests failed!\n", failureCount); // fprintf(stderr, "TOTAL ALLOCATIONS: %d\n", getAllocations()); // fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes()); return failureCount; } #endif fn(segfault, "seg", "Induces a segfault." ) { int* p = NULL; return numberObject(*p); } #ifdef STANDALONE #define pf(_func) buildFuncSym(_func ## Symbol, &(_func), _func ## Doc, _func ## Tests, array_length(_func ## Tests)) #else #define pf(_func) buildFuncSym(_func ## Symbol, &(_func)) #endif struct Environment defaultEnv() { #ifndef STANDALONE int helpInitialized = 0; #endif if (!helpInitialized) { dictionary = (struct Dictionary) { .structCount = 0, .structCapacity = 8, .structDefs = malloc(sizeof(struct StructDef) * 8), }; } struct Environment e = { .outer = NULL, .strings = malloc(sizeof(char*) * MAX_ENV_ELM), .objects = malloc(sizeof(Object) * MAX_ENV_ELM), .capacity = MAX_ENV_ELM, .refs = 1, }; struct symFunc symFuncs[] = { pf(def), pf(add), pf(sub), pf(mul), pf(dvi), pf(mod), pf(equ), pf(greaterThan), pf(lessThan), pf(and), pf(or), pf(catObjects), pf(filter), pf(len), pf(append), pf(prepend), pf(reduce), pf(mapO), pf(at), pf(rest), pf(charAt), pf(isNum), pf(isList), pf(isString), pf(isErr), pf(charVal), pf(parseEvalO), pf(structAccess), pf(getTime), #ifndef LOW_MEM pf(reverse), #endif #ifdef WEBSERVER pf(addGetRoute), pf(addPostRoute), pf(startServer), #endif #ifdef STANDALONE pf(segfault), pf(print), pf(numToChar), pf(printEnvO), pf(systemCall), pf(loadFile), pf(cd), pf(cwd), pf(takeInput), pf(readFileToObject), pf(getEnvVar), pf(help) #endif }; unsigned i; for (i = 0; i < sizeof(symFuncs) / sizeof(symFuncs[0]); i++) { addFunc(symFuncs[i].sym, symFuncs[i].func, &e, i); } for (int j = i; j < e.capacity; j++) { e.strings[j] = NULL; } helpInitialized = 1; return e; } int getStructIndex(const char* name) { for (int i = 0; i < dictionary.structCount; i++) { if (strcmp(name, dictionary.structDefs[i].name) == 0) { return i; } } return -1; } struct StructDef* getStructAt(int i) { return &dictionary.structDefs[i]; } void addStructDef(struct StructDef def) { dictionary.structDefs[dictionary.structCount] = def; dictionary.structCount += 1; if (dictionary.structCount == dictionary.structCapacity) { struct StructDef* prev = dictionary.structDefs; int prevCapacity = dictionary.structCapacity; dictionary.structCapacity *= 2; dictionary.structDefs = malloc(sizeof(struct StructDef) * dictionary.structCapacity); for (int i = 0; i < prevCapacity; i++) { dictionary.structDefs[i] = prev[i]; } free(prev); } }