260 lines
6.7 KiB
C
260 lines
6.7 KiB
C
#include "env.h"
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#include "pebblisp.h"
|
||
|
||
Object fetchFromEnvironment(const char* name, struct Environment* env)
|
||
{
|
||
if (!env) {
|
||
return errorObject(NULL_ENV);
|
||
}
|
||
|
||
if (env->size == 0) {
|
||
if (env->outer) {
|
||
return fetchFromEnvironment(name, env->outer);
|
||
} else {
|
||
return errorObject(EMPTY_ENV);
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < env->size; i++) {
|
||
if (env->strings[i] == NULL) {
|
||
printd("Try %d (NULL)\n", i);
|
||
break;
|
||
}
|
||
|
||
printd("Try %d (%s)\n", i, env->strings[i]);
|
||
debugObj(&env->objects[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);
|
||
}
|
||
|
||
struct Environment envForLambda(const Object* params, const Object* arg_forms,
|
||
struct Environment* outer)
|
||
{
|
||
int paramCount = listLength(params);
|
||
|
||
struct Environment env = {.outer = outer,
|
||
.strings = NULL,
|
||
.objects = NULL,
|
||
.structCount = 0,
|
||
.size = paramCount};
|
||
|
||
if (paramCount == 0) {
|
||
return env;
|
||
}
|
||
|
||
env.strings = calloc(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 = eval(march, outer);
|
||
addToEnv(&env, newObjName, newEnvObj); // Could use eval_forms?
|
||
cleanObject(&newEnvObj);
|
||
march = march->forward;
|
||
}
|
||
|
||
return env;
|
||
}
|
||
|
||
// Determine if a given symbol exists in the given environment chain
|
||
//
|
||
// Modifies `env` to the environment that the symbol was found in, only if the
|
||
// symbol was found
|
||
//
|
||
// Returns the index of the symbol in the environment, if found. Otherwise, -1
|
||
int findInEnv(struct Environment** env, const char* name)
|
||
{
|
||
struct Environment* temp_env = *env;
|
||
while (temp_env) {
|
||
for (int i = 0; i < temp_env->size; i++) {
|
||
if (temp_env->strings[i] == NULL) {
|
||
break;
|
||
}
|
||
if (strcmp(temp_env->strings[i], name) == 0) {
|
||
*env = temp_env;
|
||
return i;
|
||
}
|
||
}
|
||
temp_env = temp_env->outer;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
// TODO Maybe extend environment using a new outer, instead of realloc
|
||
void addToEnv(struct Environment* env, const char* name, const Object obj)
|
||
{
|
||
// int sym_pos = findInEnv(&env, name);
|
||
// if(sym_pos != -1) {
|
||
// Object o = cloneObject(obj);
|
||
// cleanObject(&env->objects[sym_pos]);
|
||
// env->objects[sym_pos] = o;
|
||
// return;
|
||
// }
|
||
|
||
int i;
|
||
struct Environment* temp_env = env;
|
||
int is_local = 1;
|
||
while (temp_env) {
|
||
for (i = 0; i < temp_env->size; i++) {
|
||
// Add *new* item to env only if we're in the local scope
|
||
if (temp_env->strings[i] == NULL) {
|
||
if (is_local) {
|
||
temp_env->strings[i] =
|
||
calloc(sizeof(char), strlen(name) + 1);
|
||
strncpy(temp_env->strings[i], name, strlen(name));
|
||
temp_env->objects[i] = cloneObject(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;
|
||
is_local = 0;
|
||
}
|
||
|
||
printd("Reallocating environment\n");
|
||
const int inc = 5;
|
||
const int old_size = env->size;
|
||
env->size += inc;
|
||
env->strings = realloc(env->strings, sizeof(char*) * env->size);
|
||
env->objects = realloc(env->objects, sizeof(Object) * env->size);
|
||
|
||
for (int j = 0; j < inc; j++) {
|
||
env->strings[old_size + j] = NULL;
|
||
}
|
||
|
||
env->strings[old_size] = malloc(strlen(name) + 1);
|
||
strncpy(env->strings[old_size], name, strlen(name) + 1);
|
||
env->objects[old_size] = cloneObject(obj);
|
||
}
|
||
|
||
void printEnv(struct Environment* env)
|
||
{
|
||
if (!env) {
|
||
printf("NULL env\n");
|
||
return;
|
||
}
|
||
for (int i = 0; i < env->size; i++) {
|
||
if (env->strings[i] == NULL) {
|
||
printf("env[%d]: NULL %p\n", i, env->strings[i]);
|
||
break;
|
||
}
|
||
printf("env[%d]: '%s' %p\n", i, env->strings[i], env->strings[i]);
|
||
printf("[1m ");
|
||
printObj(&env->objects[i]);
|
||
printf("[0m");
|
||
}
|
||
}
|
||
|
||
void addFunc(const char* name,
|
||
Object (* func)(Object, Object, struct Environment*),
|
||
struct Environment* env)
|
||
{
|
||
Object o = newObject(TYPE_FUNC);
|
||
o.func = func;
|
||
addToEnv(env, name, o);
|
||
}
|
||
|
||
void deleteEnv(struct Environment* e)
|
||
{
|
||
if (e->strings) {
|
||
int i = 0;
|
||
while (e->strings[i]) {
|
||
free(e->strings[i]);
|
||
cleanObject(&e->objects[i]);
|
||
i++;
|
||
}
|
||
free(e->strings);
|
||
e->strings = NULL;
|
||
|
||
free(e->objects);
|
||
e->objects = NULL;
|
||
}
|
||
}
|
||
|
||
struct symFunc {
|
||
const char* sym;
|
||
|
||
Object (* func)(Object, Object, struct Environment*);
|
||
};
|
||
|
||
struct Environment defaultEnv()
|
||
{
|
||
char** strings = calloc(sizeof(char*), MAX_ENV_ELM);
|
||
Object* objects = malloc(sizeof(Object) * MAX_ENV_ELM);
|
||
char size = MAX_ENV_ELM;
|
||
|
||
struct Environment e = {
|
||
.outer = NULL,
|
||
.strings = strings,
|
||
.objects = objects,
|
||
.size = size,
|
||
.structCount = 0,
|
||
};
|
||
|
||
struct symFunc symFuncs[] = {
|
||
{"+", &add},
|
||
{"-", &sub},
|
||
{"*", &mul},
|
||
{"/", &dvi},
|
||
{"%", &mod},
|
||
{"=", &equ},
|
||
{">", >h},
|
||
{"<", <h},
|
||
{"&", &and},
|
||
{"|", &or},
|
||
{"cat", &catObjects},
|
||
{"fil", &filter},
|
||
{"len", &len},
|
||
{"ap", &append},
|
||
{"pre", &prepend},
|
||
{"at", &at},
|
||
{"rest", &rest},
|
||
{"chat", &charAt},
|
||
#ifndef LOW_MEM
|
||
{"rev", &reverse},
|
||
#endif
|
||
{"isnum", &isNum},
|
||
{"isstr", &isString},
|
||
{"iserr", &isErr},
|
||
{"char", &charVal},
|
||
{"eval", &parseEvalO},
|
||
#ifdef STANDALONE
|
||
{"prn", &print},
|
||
{"pch", &pChar},
|
||
{"penv", &printEnvO},
|
||
//{"sys", &systemCall},
|
||
{"loadfile", &loadFile},
|
||
{"inp", &takeInput},
|
||
#endif
|
||
};
|
||
|
||
for (unsigned i = 0; i < sizeof(symFuncs) / sizeof(symFuncs[0]); i++) {
|
||
addFunc(symFuncs[i].sym, symFuncs[i].func, &e);
|
||
}
|
||
|
||
return e;
|
||
}
|