1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-15 05:04:49 +00:00
janet/core/compile.c

354 lines
12 KiB
C
Raw Normal View History

/*
* Copyright (c) 2017 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <dst/dst.h>
2017-12-15 00:33:45 +00:00
#include "compile.h"
2017-12-15 00:33:45 +00:00
static void dst_compile_cleanup(DstCompiler *c) {
}
DstSlot dst_compile_error(DstCompiler *c, const DstValue *sourcemap, const uint8_t *m) {
DstSlot ret;
c->error_start = dst_unwrap_integer(sourcemap[0]);
c->error_end = dst_unwrap_integer(sourcemap[1]);
c->error = m;
ret.flags = DST_SLOT_ERROR;
ret.index = 0;
ret.constant = dst_wrap_nil();
return ret;
}
DstSlot dst_compile_cerror(DstCompiler *c, const DstValue *sourcemap, const char *m) {
return dst_compile_error(c, sourcemap, dst_cstring(m));
}
/* Use these to get sub options. They will traverse the source map so
* compiler errors make sense. Then modify the returned options. */
DstFormOptions dst_compile_getopts_index(DstFormOptions opts, int32_t index) {
DstCompiler *c = opts.compiler;
const DstValue *sourcemap = dst_parse_submap_index(opts.sourcemap, index);
DstValue nextval = dst_getindex(opts.x, index);
opts.x = nextval;
opts.sourcemap = sourcemap;
return opts;
}
DstFormOptions dst_compile_getopts_key(DstFormOptions opts, DstValue key) {
DstCompiler *c = opts.compiler;
const DstValue *sourcemap = dst_parse_submap_key(opts.sourcemap, key);
opts.x = key;
opts.sourcemap = sourcemap;
return opts;
}
DstFormOptions dst_compile_getopts_value(DstFormOptions opts, DstValue key) {
DstCompiler *c = opts.compiler;
const DstValue *sourcemap = dst_parse_submap_value(opts.sourcemap, key);
DstValue nextval = dst_get(opts.x, key);
opts.x = nextval;
opts.sourcemap = sourcemap;
return opts;
}
/* Eneter a new scope */
void dst_compile_scope(DstCompiler *c, int newfn) {
DstScope *scope;
if (c->scopecap < c->scopecount) {
c->scopes = realloc(c->scopes, 2 * sizeof(DstScope) * c->scopecount + 2);
if (NULL == c->scope) {
DST_OUT_OF_MEMORY;
}
}
scope = c->scopes + c->scopecount++;
dst_array_init(&scope->constants, 0);
dst_table_init(&scope->symbols, 4);
scope->envs = NULL;
scope->envcount = 0;
scope->envcap = 0;
scope->slots = NULL;
scope->slotcount = 0;
scope->slotcap = 0;
scope->freeslots = NULL;
scope->freeslotcount = 0;
scope->freeslotcap = 0;
scope->buffer_offset = c->buffer.count;
scope->nextslot = 0;
scope->lastslot = -1;
scope->flags = newfn ? DST_SCOPE_FUNCTION : 0;
}
DstSlot dst_slot_nil() {
DstSlot ret;
ret.index = 0;
ret.flags = (1 << DST_TYPE_NIL) | DST_SLOT_CONSTANT;
ret.constant = dst_wrap_nil();
return ret;
}
/* Leave a scope. Does not build closure*/
void dst_compile_popscope(DstCompiler *c) {
DstScope *scope;
DstSlot ret;
dst_assert(c->scopecount, "could not pop scope");
scope = c->scopes + --c->scopecount;
/* Move free slots to parent scope if not a new function */
if (!(scope->flags & DST_SCOPE_FUNCTION) && c->scopecount) {
int32_t i;
int32_t newcount;
DstScope *topscope = c->scopes + c->scopecount - 1;
topscope->nextslot = scope->nextslot;
newcount = topscope->freeslotcount + scope->freeslotcount;
if (topscope->freeslotcap < newcount) {
topscope->freeslots = realloc(topscope->freeslot, sizeof(int32_t) * newcount);
if (NULL == topscope->freeslots) {
DST_OUT_OF_MEMORY;
}
topscope->freeslotcap = newcount;
}
memcpy(
topscope->freeslots + topescope->freeslotcount,
scope->freeslots,
sizeof(int32_t) * scope->freeslotcount);
topscope->freeslotcount = newcount;
}
dst_table_deinit(&scope->symbols);
dst_array_deinit(&scope->constants);
free(scope->slots);
free(scope->freeslots);
free(scope->envs);
return ret;
}
#define dst_compile_topscope(c) ((c)->scopes + (c)->scopecount - 1)
/* Allocate a slot space */
static int32_t dst_compile_slotalloc(DstCompiler *c) {
DstScope *scope = dst_compile_topscope(c);
if (scope->freeslotcount == 0) {
return scope->nextslot++;
} else {
return scope->freeslots[--scope->freeslotcount];
}
}
int dst_compile_slotmatch(DstFormOptions opts, DstSlot slot) {
return opts.type & slot.type;
}
DstSlot dst_compile_normalslot(DstCompiler *c, uint32_t flags) {
DstSlot ret;
int32_t index = dst_compile_slotalloc(c);
ret.flags = flags;
ret.constant = dst_wrap_nil();
ret.index = index;
ret.envindex = 0;
return ret;
}
DstSlot dst_compile_constantslot(DstCompiler *c, DstValue x) {
DstSlot ret;
ret.flags = (1 << dst_type(x)) | DST_SLOT_CONSTANT;
ret.index = -1;
ret.constant = x;
ret.envindex = 0;
return ret;
}
/* Free a single slot */
void dst_compile_freeslot(DstCompiler *c, DstSlot slot) {
DstScope *scope = dst_compile_topscope(c);
int32_t newcount = scope->freeslotcount + 1;
if (slot.flags & (DST_SLOT_CONSTANT | DST_SLOT_ERROR))
return;
if (scope->freeslotcap < newcount) {
int32_t newcap = 2 * newcount;
scope->freeslots = realloc(scope->freeslots, sizeof(int32_t) * newcap);
if (NULL == scope->freeslots) {
DST_OUT_OF_MEMORY;
}
scope->freeslotcap = newcap;
}
scope->freeslots[scope->freeslotcount] = slot.index;
scope->freeslotcount = newcount;
}
/* Free an array of slots */
void dst_compile_freeslotarray(DstCompiler *c, DstArray *slots) {
int32_t i;
for (i = 0; i < slots->count; i++) {
dst_compile_freeslot(c, slots->data[i]);
}
}
/*
* The mechanism for passing environments to to closures is a bit complicated,
* but ensures a few properties.
* * Environments are on the stack unless they need to be closurized
* * Environments can be shared between closures
* * A single closure can access any of multiple parent environments in constant time (no linked lists)
*
* FuncDefs all have a list of a environment indices that are inherited from the
* parent function, as well as a flag indicating if the closures own stack variables
* are needed in a nested closure. The list of indices says which of the parent environments
* go into which environment slot for the new closure. This allows closures to use whatever environments
* they need to, as well as pass these environments to sub closures. To access the direct parent's environment,
* the FuncDef must copy the 0th parent environment. If a closure does not need to export it's own stack
* variables for creating closures, it must keep the 0th entry in the env table to NULL.
*
* TODO - check if this code is bottle neck and search for better data structures.
2017-12-08 20:57:02 +00:00
*/
2017-12-15 00:33:45 +00:00
/* Allow searching for symbols. Return information about the symbol */
DstSlot dst_compile_resolve(
DstCompiler *c,
const DstValue *sourcemap,
const uint8_t *sym) {
DstSlot ret;
DstScope *scope = dst_compile_topscope(c);
int32_t env_index = 0;
int foundlocal;
/* Search scopes for symbol, starting from top */
while (scope >= c->scopes) {
DstValue check = dst_table_get(scope->symbols, dst_wrap_symbol(sym));
if (dst_checktype(check, DST_INTEGER)) {
ret = scope->slots[dst_unwrap_integer(check)];
goto found;
}
scope--;
}
/* Symbol not found */
return dst_compile_error(c, sourcemap, dst_formatc("unknown symbol %q", sym));
/* Symbol was found */
found:
/* Constants and errors can be returned immediately (they are stateless) */
if (ret.flags & (DST_SLOT_CONSTANT | DST_SLOT_ERROR))
return ret;
/* non-local scope needs to expose its environment */
foundlocal = scope == dst_compile_topscope(c);
if (!foundlocal) {
scope->flags |= DST_SCOPE_ENV;
if (scope->envcount < 1) {
scope->envcount = 1;
scope->envs = malloc(sizeof(int32_t) * 10);
if (NULL == scope->envs) {
DST_OUT_OF_MEMORY;
}
scope->envcap = 10;
scope->envs[0] = -1;
}
scope++;
}
/* Propogate env up to current scope */
while (scope <= dst_compile_topscope(c)) {
int32_t j;
int32_t newcount = scope->envcount + 1;
int scopefound = 0;
/* Check if scope already has env. If so, break */
for (j = 1; j < scope->envcount; j++) {
if (scope->envs[j] == env_index) {
scopefound = 1;
env_index = j;
break;
}
}
if (!scopefound) {
env_index = scope->envcount;
/* Ensure capacity for adding scope */
if (newcount > scope->envcap) {
int32_t newcap = 2 * newcount;
scope->envs = realloc(scope->envs, sizeof(int32_t) * newcap);
if (NULL == scope->envs) {
DST_OUT_OF_MEMORY;
}
scope->envcap = newcap;
}
scope->envs[scope->envcount] = env_index;
scope->envcount = newcount;
}
scope++;
}
/* Take the slot from the upper scope, and set its envindex before returning. */
if (!foundlocal) {
ret.envindex = env_index;
}
return ret;
}
/* Compile an array */
/* Compile a single value */
DstSlot dst_compile_value(DstFormOptions opts) {
DstSlot ret;
if (opts.compiler->recursion_guard <= 0) {
return dst_compiler_cerror(opts.compiler, opts.sourcemap, "recursed too deeply");
}
opts.compiler->recursion_guard--;
switch (dst_type(opts.x)) {
default:
ret = dst_compile_constantslot(opts.x);
break;
case DST_SYMBOL:
{
const uint8_t *sym = dst_unwrap_symbol(opts.x);
if (dst_string_length(sym) > 0 && sym[0] != ':')
ret = dst_compile_resolve(opts.compiler, opts.sourcemap, sym);
else
ret = dst_compile_constantslot(opts.x);
break;
}
case DST_TUPLE:
ret = dst_compile_tuple(opts);
break;
case DST_ARRAY:
ret = dst_compile_array(opts);
break;
case DST_STRUCT:
ret = dst_compile_struct(opts);
break;
case DST_TABLE:
ret = dst_compile_table(opts);
break;
}
opts.compiler->recursion_guard++;
return ret;
}
DstSlot dst_compile_targetslot(DstFormOptions opts, DstSlot s);
/* Coerce any slot into the target slot. If no target is specified, return
* the slot unaltered. Otherwise, move and load upvalues as necesarry to set the slot. */
DstSlot dst_compile_coercetargetslot(DstFormOptions opts, DstSlot s);