Environment#

Environment (not to be mixed with context) represent current state of interpreter instance. It contains reference to memory, input stream and output stream. To enable tail call optimization, environment is also used as storage for next expression evaluation.

Main method responsible for environment evaluation is lsp_env_resolve. This function implements evaluation loop (also known as trampoline), which iteratively evaluates sequence of expressions. Evaluation of single expression can result in direct data value (which is registered with lsp_env_set_result_value function) or can be delegated to execution of another expression (which is registered with lsp_env_set_result_eval function). Evaluation loop (trampoline) repeats expression evaluation until resulting data value is fully resolved.

Source code#

env.h#

#ifndef LISP16_ENV_H
#define LISP16_ENV_H

#include "mem.h"
#include "stream.h"


typedef struct {
    lsp_mem_t *m;
    lsp_in_stream_t *in;
    lsp_out_stream_t *out;

    // internal
    struct {
        lsp_bool_t is_value;
        lsp_addr_t ctx;
        lsp_addr_t value;
    } result;
} lsp_env_t;


void lsp_env_init(lsp_env_t *e, lsp_mem_t *m, lsp_in_stream_t *in,
                  lsp_out_stream_t *out);
lsp_status_t lsp_env_set_result_value(lsp_env_t *e, lsp_addr_t value);
lsp_status_t lsp_env_set_result_eval(lsp_env_t *e, lsp_addr_t ctx,
                                     lsp_addr_t value);
lsp_status_t lsp_env_resolve(lsp_env_t *e, lsp_addr_t ctx, lsp_addr_t value,
                             lsp_addr_t *result);

#endif

env.c#

#include "env.h"
#include "ctx.h"
#include "eval.h"


static void init_result(lsp_env_t *e) {
    e->result.is_value = true;
    e->result.ctx = e->m->nil;
    e->result.value = e->m->nil;
}


static void release_result(lsp_env_t *e) {
    lsp_mem_dec_ref(e->m, e->result.ctx);
    lsp_mem_dec_ref(e->m, e->result.value);

    init_result(e);
}


void lsp_env_init(lsp_env_t *e, lsp_mem_t *m, lsp_in_stream_t *in,
                  lsp_out_stream_t *out) {
    e->m = m;
    e->in = in;
    e->out = out;

    init_result(e);
}


lsp_status_t lsp_env_set_result_value(lsp_env_t *e, lsp_addr_t value) {
    release_result(e);

    lsp_status_t status = lsp_mem_inc_ref(e->m, value);
    if (status != LSP_SUCCESS)
        return status;

    e->result.is_value = true;
    e->result.value = value;
    return LSP_SUCCESS;
}


lsp_status_t lsp_env_set_result_eval(lsp_env_t *e, lsp_addr_t ctx,
                                     lsp_addr_t value) {
    release_result(e);

    lsp_status_t status = lsp_mem_inc_ref(e->m, ctx);
    if (status != LSP_SUCCESS)
        return status;

    status = lsp_mem_inc_ref(e->m, value);
    if (status != LSP_SUCCESS) {
        lsp_mem_dec_ref(e->m, ctx);
        return status;
    }

    e->result.is_value = false;
    e->result.ctx = ctx;
    e->result.value = value;
    return LSP_SUCCESS;
}


lsp_status_t lsp_env_resolve(lsp_env_t *e, lsp_addr_t ctx, lsp_addr_t value,
                             lsp_addr_t *result) {
    lsp_status_t status = lsp_env_set_result_eval(e, ctx, value);
    if (status != LSP_SUCCESS)
        return status;

    while (!e->result.is_value) {
        lsp_addr_t eval_ctx = e->result.ctx;
        lsp_addr_t eval_value = e->result.value;
        init_result(e);

        status = lsp_eval(e, eval_ctx, eval_value);
        lsp_mem_dec_ref(e->m, eval_ctx);
        lsp_mem_dec_ref(e->m, eval_value);
        if (status != LSP_SUCCESS)
            return status;
    }

    *result = e->result.value;
    init_result(e);
    return LSP_SUCCESS;
}