138 lines
3.3 KiB
C
138 lines
3.3 KiB
C
#include <stdlib.h>
|
|
|
|
#include "lang.h"
|
|
#include "util.h"
|
|
|
|
lenv* lenv_new() {
|
|
lenv* env = calloc(1, sizeof(lenv));
|
|
env->count = 0;
|
|
env->parent = NULL;
|
|
env->syms = NULL;
|
|
env->loaded_files_count = 0;
|
|
env->loaded_files = NULL;
|
|
return env;
|
|
}
|
|
void lenv_delete(lenv* env) {
|
|
for(int i = 0; i < env->count; i++) {
|
|
symtab_delete(env->syms[i]);
|
|
}
|
|
for(int i = 0; i < env->loaded_files_count; i++) {
|
|
free(env->loaded_files[i]);
|
|
}
|
|
if (env->loaded_files_count > 0) {
|
|
free(env->loaded_files);
|
|
}
|
|
if (env->syms != NULL) {
|
|
free(env->syms);
|
|
}
|
|
env->parent = NULL;
|
|
free(env);
|
|
}
|
|
lenv* lenv_copy(lenv* env) {
|
|
lenv* new = lenv_new();
|
|
new->parent = env;
|
|
new->syms = calloc(env->count, sizeof(symtab*));
|
|
for(int i = 0; i < env->count; i++) {
|
|
new->syms[i] = symtab_copy(env->syms[i]);
|
|
}
|
|
return new;
|
|
}
|
|
|
|
int lenv_compare_symtabs(const void *lhs, const void *rhs) {
|
|
const struct symtab* l = *(const struct symtab**)lhs;
|
|
const struct symtab* r = *(const struct symtab**)rhs;
|
|
|
|
return strcmp(l->sym, r->sym);
|
|
}
|
|
|
|
void lenv_sort(lenv* env) {
|
|
qsort(env->syms, env->count, sizeof(symtab*), lenv_compare_symtabs);
|
|
}
|
|
|
|
symtab* lenv_search(lenv* env, char* sym) {
|
|
if (env->count == 0) {
|
|
return NULL;
|
|
}
|
|
symtab* searchElem = symtab_new(sym, NULL);
|
|
|
|
symtab** searchComp = calloc(1, sizeof(symtab*));
|
|
searchComp[0] = searchElem;
|
|
|
|
symtab** result = bsearch(searchComp, env->syms, env->count, sizeof(symtab*), lenv_compare_symtabs);
|
|
|
|
free(searchComp);
|
|
symtab_delete(searchElem);
|
|
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
return *result;
|
|
}
|
|
lenv* lenv_get_root(lenv* env) {
|
|
if (env->parent == NULL) {
|
|
return env;
|
|
} else {
|
|
return lenv_get_root(env->parent);
|
|
}
|
|
}
|
|
|
|
lval* lenv_get(lenv* env, lval* sym) {
|
|
LASSERT(sym, sym->type == LVAL_SYM, LERR_BAD_OP, "Expected symbol");
|
|
|
|
symtab* result = lenv_search(env, sym->data.sym);
|
|
|
|
if (result != NULL) {
|
|
return lval_copy(result->lval);
|
|
} else {
|
|
if (env->parent != NULL) {
|
|
return lenv_get(env->parent, sym);
|
|
} else {
|
|
return lval_err_detail(LERR_BAD_SYM, "Unbound Symbol '%s'", sym->data.sym);
|
|
}
|
|
}
|
|
}
|
|
|
|
void lenv_put(lenv* env, lval* key, lval* val) {
|
|
symtab* result = lenv_search(env, key->data.sym);
|
|
if (result != NULL) {
|
|
lval_delete(result->lval);
|
|
result->lval = lval_copy(val);
|
|
|
|
lenv_sort(env);
|
|
return;
|
|
}
|
|
|
|
env->count ++;
|
|
|
|
env->syms = realloc(env->syms, sizeof(symtab*) * env->count);
|
|
|
|
env->syms[env->count-1] = symtab_new(key->data.sym, val);
|
|
|
|
lenv_sort(env);
|
|
}
|
|
void lenv_def(lenv* env, lval* key, lval* val) {
|
|
while(env->parent != NULL) {
|
|
env = env->parent;
|
|
}
|
|
lenv_put(env, key, val);
|
|
}
|
|
|
|
symtab* symtab_new(char* sym, lval* lval) {
|
|
symtab* new = calloc(1, sizeof(symtab));
|
|
new->lval = lval == NULL ? NULL : lval_copy(lval);
|
|
new->sym = strdup(sym);
|
|
return new;
|
|
}
|
|
void symtab_delete(symtab* symtab) {
|
|
if (symtab->lval != NULL) {
|
|
lval_delete(symtab->lval);
|
|
}
|
|
free(symtab->sym);
|
|
free(symtab);
|
|
}
|
|
symtab* symtab_copy(symtab* symtab) {
|
|
if (symtab == NULL) {
|
|
return NULL;
|
|
}
|
|
return symtab_new(symtab->sym, symtab->lval);
|
|
} |