pebblisp/src/env.c

260 lines
6.7 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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(" ");
printObj(&env->objects[i]);
printf("");
}
}
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},
{">", &gth},
{"<", &lth},
{"&", &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;
}