picrin/src/codegen.c

1115 lines
24 KiB
C
Raw Normal View History

2014-01-17 06:58:31 -05:00
/**
* See Copyright Notice in picrin.h
*/
2013-10-20 04:06:47 -04:00
#include <stdio.h>
2014-01-12 10:50:45 -05:00
#include <assert.h>
2013-10-20 04:06:47 -04:00
#include "picrin.h"
#include "picrin/pair.h"
#include "picrin/irep.h"
#include "picrin/proc.h"
#include "picrin/lib.h"
#include "picrin/macro.h"
#include "xhash/xhash.h"
2013-10-20 04:06:47 -04:00
2014-01-08 01:22:23 -05:00
#if PIC_NONE_IS_FALSE
# define OP_PUSHNONE OP_PUSHFALSE
#else
# error enable PIC_NONE_IS_FALSE
#endif
2013-10-23 13:51:02 -04:00
#define FALLTHROUGH ((void)0)
2013-10-23 02:52:14 -04:00
typedef struct codegen_scope {
struct codegen_scope *up;
/* local variables are 1-indexed, 0 is reserved for the callee */
struct xhash *local_tbl;
/* rest args variable is counted at localc */
size_t argc, localc;
2013-11-04 19:14:21 -05:00
/* if local var i is captured, then dirty_flags[i] == 1 */
int *dirty_flags;
2013-10-27 05:13:36 -04:00
bool varg;
2013-10-23 02:52:14 -04:00
} codegen_scope;
2013-10-23 02:52:14 -04:00
static codegen_scope *
new_global_scope(pic_state *pic)
{
2013-10-23 02:52:14 -04:00
codegen_scope *scope;
2013-10-23 02:52:14 -04:00
scope = (codegen_scope *)pic_alloc(pic, sizeof(codegen_scope));
scope->up = NULL;
scope->local_tbl = pic->global_tbl;
scope->argc = -1;
scope->localc = -1;
2013-11-04 19:14:21 -05:00
scope->dirty_flags = NULL;
2013-10-27 05:13:36 -04:00
scope->varg = false;
return scope;
}
2013-10-23 02:52:14 -04:00
static codegen_scope *
new_local_scope(pic_state *pic, pic_value args, codegen_scope *scope)
2013-10-20 04:06:47 -04:00
{
2013-10-23 02:52:14 -04:00
codegen_scope *new_scope;
2013-10-20 04:06:47 -04:00
pic_value v;
int i, l;
struct xhash *x;
2013-10-23 02:52:14 -04:00
new_scope = (codegen_scope *)pic_alloc(pic, sizeof(codegen_scope));
new_scope->up = scope;
new_scope->local_tbl = x = xh_new();
2013-10-27 05:13:36 -04:00
new_scope->varg = false;
i = 1; l = 0;
2013-10-27 05:13:36 -04:00
for (v = args; pic_pair_p(v); v = pic_cdr(pic, v)) {
pic_value sym;
sym = pic_car(pic, v);
2013-10-28 13:11:31 -04:00
xh_put(x, pic_symbol_name(pic, pic_sym(sym)), i++);
}
2013-10-27 05:13:36 -04:00
if (pic_nil_p(v)) {
/* pass */
}
else if (pic_symbol_p(v)) {
new_scope->varg = true;
xh_put(x, pic_symbol_name(pic, pic_sym(v)), i + l++);
2013-10-27 05:13:36 -04:00
}
else {
pic_error(pic, "logic flaw");
}
new_scope->argc = i;
new_scope->localc = l;
2013-11-04 19:14:21 -05:00
new_scope->dirty_flags = (int *)pic_calloc(pic, i + l, sizeof(int));
return new_scope;
}
static void
2013-10-23 04:25:39 -04:00
destroy_scope(pic_state *pic, codegen_scope *scope)
{
if (scope->up) {
xh_destory(scope->local_tbl);
2013-11-04 19:14:21 -05:00
pic_free(pic, scope->dirty_flags);
}
pic_free(pic, scope);
}
2013-10-23 04:25:39 -04:00
static struct pic_irep *
new_irep(pic_state *pic)
{
struct pic_irep *irep;
irep = (struct pic_irep *)pic_alloc(pic, sizeof(struct pic_irep));
irep->code = (struct pic_code *)pic_alloc(pic, sizeof(struct pic_code) * 1024);
irep->clen = 0;
irep->ccapa = 1024;
irep->argc = -1;
irep->localc = -1;
2013-10-27 05:13:36 -04:00
irep->varg = false;
2013-10-23 04:25:39 -04:00
return irep;
}
typedef struct codegen_state {
pic_state *pic;
codegen_scope *scope;
struct pic_irep *irep;
} codegen_state;
static codegen_state *
new_codegen_state(pic_state *pic)
{
codegen_state *state;
state = (codegen_state *)pic_alloc(pic, sizeof(codegen_state));
state->pic = pic;
state->scope = new_global_scope(pic);
return state;
}
static void
destroy_codegen_state(pic_state *pic, codegen_state *state)
{
destroy_scope(pic, state->scope);
pic_free(pic, state);
}
static codegen_scope *
2013-10-23 04:25:39 -04:00
scope_lookup(codegen_state *state, const char *key, int *depth, int *idx)
{
2013-10-23 04:25:39 -04:00
codegen_scope *scope = state->scope;
struct xh_entry *e;
2013-10-20 04:06:47 -04:00
int d = 0;
enter:
e = xh_get(scope->local_tbl, key);
2013-10-30 03:37:43 -04:00
if (e && e->val >= 0) {
if (scope->up == NULL) { /* global */
2013-10-20 04:06:47 -04:00
*depth = -1;
}
else { /* non-global */
*depth = d;
}
*idx = e->val;
return scope;
2013-10-20 04:06:47 -04:00
}
if (scope->up) {
scope = scope->up;
2013-10-20 04:06:47 -04:00
++d;
goto enter;
}
return NULL;
2013-10-20 04:06:47 -04:00
}
static int
scope_global_define(pic_state *pic, const char *name)
2013-10-20 04:06:47 -04:00
{
struct xh_entry *e;
2013-10-20 04:06:47 -04:00
if ((e = xh_get(pic->global_tbl, name))) {
2013-10-24 09:29:40 -04:00
pic_warn(pic, "redefining global");
return e->val;
2013-10-20 04:06:47 -04:00
}
e = xh_put(pic->global_tbl, name, pic->glen++);
if (pic->glen >= pic->gcapa) {
pic_error(pic, "global table overflow");
2013-10-20 04:06:47 -04:00
}
return e->val;
2013-10-20 04:06:47 -04:00
}
2013-11-13 04:08:22 -05:00
static int
scope_local_define(pic_state *pic, const char *name, codegen_scope *scope)
{
struct xh_entry *e;
e = xh_put(scope->local_tbl, name, scope->argc + scope->localc++);
2013-11-16 23:10:36 -05:00
scope->dirty_flags = (int *)pic_realloc(pic, scope->dirty_flags, (scope->argc + scope->localc) * sizeof(int));
scope->dirty_flags[e->val] = 0;
2013-11-13 04:08:22 -05:00
return e->val;
}
static bool
scope_is_global(codegen_scope *scope)
{
return scope->up == NULL;
}
2013-10-29 21:04:23 -04:00
static void codegen_call(codegen_state *, pic_value, bool);
2013-10-23 04:26:02 -04:00
static struct pic_irep *codegen_lambda(codegen_state *, pic_value);
2013-10-20 04:06:47 -04:00
static void
2013-10-29 21:04:23 -04:00
codegen(codegen_state *state, pic_value obj, bool tailpos)
2013-10-20 04:06:47 -04:00
{
2013-10-23 04:25:39 -04:00
pic_state *pic = state->pic;
struct pic_irep *irep = state->irep;
2013-10-20 04:06:47 -04:00
switch (pic_type(obj)) {
case PIC_TT_SYMBOL: {
codegen_scope *s;
2013-12-03 09:10:46 -05:00
int depth = -1, idx = -1;
const char *name;
2013-10-20 04:06:47 -04:00
2013-10-28 13:11:31 -04:00
name = pic_symbol_name(pic, pic_sym(obj));
s = scope_lookup(state, name, &depth, &idx);
if (! s) {
#if DEBUG
printf("%s\n", name);
#endif
pic_error(pic, "symbol: unbound variable");
2013-10-20 04:06:47 -04:00
}
2013-10-23 13:51:02 -04:00
switch (depth) {
case -1: /* global */
2013-10-20 04:06:47 -04:00
irep->code[irep->clen].insn = OP_GREF;
irep->code[irep->clen].u.i = idx;
irep->clen++;
2013-10-23 13:51:02 -04:00
break;
default: /* nonlocal */
2013-11-04 19:14:21 -05:00
s->dirty_flags[idx] = 1;
2013-10-23 13:51:02 -04:00
/* at this stage, lref and cref are not distinguished */
FALLTHROUGH;
case 0: /* local */
irep->code[irep->clen].insn = OP_CREF;
2013-11-04 21:32:09 -05:00
irep->code[irep->clen].u.r.depth = depth;
irep->code[irep->clen].u.r.idx = idx;
irep->clen++;
2013-10-23 13:51:02 -04:00
break;
2013-10-20 04:06:47 -04:00
}
break;
}
case PIC_TT_PAIR: {
pic_value proc;
if (! pic_list_p(pic, obj)) {
pic_error(pic, "invalid expression given");
}
2013-10-20 04:06:47 -04:00
proc = pic_car(pic, obj);
2013-10-28 13:11:31 -04:00
if (pic_symbol_p(proc)) {
pic_sym sym = pic_sym(proc);
2013-10-20 04:06:47 -04:00
2013-10-28 13:11:31 -04:00
if (sym == pic->sDEFINE) {
int idx;
pic_value var, val;
2013-11-13 04:08:22 -05:00
codegen_scope *s;
2013-10-20 04:06:47 -04:00
if (pic_length(pic, obj) < 2) {
pic_error(pic, "syntax error");
}
2013-10-28 13:11:31 -04:00
var = pic_car(pic, pic_cdr(pic, obj));
if (pic_pair_p(var)) {
val = pic_cons(pic, pic_symbol_value(pic->sLAMBDA),
pic_cons(pic, pic_cdr(pic, var),
pic_cdr(pic, pic_cdr(pic, obj))));
var = pic_car(pic, var);
}
else {
if (pic_length(pic, obj) != 3) {
pic_error(pic, "syntax error");
}
val = pic_car(pic, pic_cdr(pic, pic_cdr(pic, obj)));
}
if (! pic_symbol_p(var)) {
pic_error(pic, "syntax error");
}
2013-11-13 04:08:22 -05:00
s = state->scope;
if (scope_is_global(s)) {
idx = scope_global_define(pic, pic_symbol_name(pic, pic_sym(var)));
codegen(state, val, false);
irep->code[irep->clen].insn = OP_GSET;
irep->code[irep->clen].u.i = idx;
irep->clen++;
2014-01-08 01:22:23 -05:00
irep->code[irep->clen].insn = OP_PUSHNONE;
2013-11-13 04:08:22 -05:00
irep->clen++;
break;
}
else {
idx = scope_local_define(pic, pic_symbol_name(pic, pic_sym(var)), s);
codegen(state, val, false);
irep->code[irep->clen].insn = OP_CSET;
irep->code[irep->clen].u.r.depth = 0;
irep->code[irep->clen].u.r.idx = idx;
2013-11-13 04:08:22 -05:00
irep->clen++;
2014-01-08 01:22:23 -05:00
irep->code[irep->clen].insn = OP_PUSHNONE;
2013-11-13 04:08:22 -05:00
irep->clen++;
break;
}
}
2013-10-28 13:11:31 -04:00
else if (sym == pic->sLAMBDA) {
2013-11-21 09:28:43 -05:00
int k;
if (pic->ilen >= pic->icapa) {
#if DEBUG
puts("irep realloced");
#endif
pic->irep = (struct pic_irep **)pic_realloc(pic, pic->irep, pic->icapa * 2);
pic->icapa *= 2;
}
k = pic->ilen++;
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_LAMBDA;
irep->code[irep->clen].u.i = k;
irep->clen++;
pic->irep[k] = codegen_lambda(state, obj);
break;
}
2013-10-28 13:11:31 -04:00
else if (sym == pic->sIF) {
int s,t;
2013-10-29 09:15:58 -04:00
pic_value if_true, if_false;
2014-01-08 01:22:23 -05:00
if_false = pic_none_value();
2013-10-29 09:15:58 -04:00
switch (pic_length(pic, obj)) {
default:
2013-10-28 13:11:31 -04:00
pic_error(pic, "syntax error");
2013-10-29 09:15:58 -04:00
break;
case 4:
if_false = pic_car(pic, pic_cdr(pic, pic_cdr(pic, pic_cdr(pic, obj))));
FALLTHROUGH;
case 3:
if_true = pic_car(pic, pic_cdr(pic, pic_cdr(pic, obj)));
2013-10-28 13:11:31 -04:00
}
2013-10-20 04:06:47 -04:00
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
2013-10-20 04:06:47 -04:00
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_JMPIF;
s = irep->clen++;
2013-10-20 04:06:47 -04:00
2013-10-28 13:11:31 -04:00
/* if false branch */
2013-10-29 21:04:23 -04:00
codegen(state, if_false, tailpos);
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_JMP;
t = irep->clen++;
2013-10-28 13:11:31 -04:00
irep->code[s].u.i = irep->clen - s;
/* if true branch */
2013-10-29 21:04:23 -04:00
codegen(state, if_true, tailpos);
2013-10-28 13:11:31 -04:00
irep->code[t].u.i = irep->clen - t;
break;
}
else if (sym == pic->sBEGIN) {
2013-10-29 21:04:23 -04:00
int i, len;
2013-10-28 13:11:31 -04:00
pic_value v, seq;
seq = pic_cdr(pic, obj);
2013-10-29 21:04:23 -04:00
len = pic_length(pic, seq);
for (i = 0; i < len; ++i) {
v = pic_car(pic, seq);
if (i + 1 >= len) {
codegen(state, v, tailpos);
}
else {
codegen(state, v, false);
irep->code[irep->clen].insn = OP_POP;
irep->clen++;
}
seq = pic_cdr(pic, seq);
2013-10-28 13:11:31 -04:00
}
break;
}
else if (sym == pic->sSETBANG) {
codegen_scope *s;
pic_value var;
2013-12-03 09:10:46 -05:00
int depth = -1, idx = -1;
2013-10-20 04:06:47 -04:00
2013-10-28 13:11:31 -04:00
if (pic_length(pic, obj) != 3) {
pic_error(pic, "syntax error");
}
2013-10-20 04:06:47 -04:00
2013-10-28 13:11:31 -04:00
var = pic_car(pic, pic_cdr(pic, obj));
if (! pic_symbol_p(var)) {
pic_error(pic, "syntax error");
}
2013-10-20 04:06:47 -04:00
2013-10-28 13:11:31 -04:00
s = scope_lookup(state, pic_symbol_name(pic, pic_sym(var)), &depth, &idx);
if (! s) {
pic_error(pic, "unbound variable");
}
2013-10-20 04:06:47 -04:00
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, pic_cdr(pic, obj))), false);
2013-10-28 13:11:31 -04:00
switch (depth) {
case -1: /* global */
irep->code[irep->clen].insn = OP_GSET;
irep->code[irep->clen].u.i = idx;
irep->clen++;
break;
default: /* nonlocal */
2013-11-04 19:14:21 -05:00
s->dirty_flags[idx] = 1;
2013-10-28 13:11:31 -04:00
/* at this stage, lset and cset are not distinguished */
FALLTHROUGH;
case 0: /* local */
irep->code[irep->clen].insn = OP_CSET;
2013-11-04 21:32:09 -05:00
irep->code[irep->clen].u.r.depth = depth;
irep->code[irep->clen].u.r.idx = idx;
2013-10-28 13:11:31 -04:00
irep->clen++;
break;
}
2013-10-20 04:06:47 -04:00
2014-01-08 01:22:23 -05:00
irep->code[irep->clen].insn = OP_PUSHNONE;
2013-10-20 04:06:47 -04:00
irep->clen++;
2013-10-28 13:11:31 -04:00
break;
2013-10-20 04:06:47 -04:00
}
2013-10-28 13:11:31 -04:00
else if (sym == pic->sQUOTE) {
int pidx;
2013-10-23 14:14:32 -04:00
2013-10-28 13:11:31 -04:00
if (pic_length(pic, obj) != 2) {
pic_error(pic, "syntax error");
}
2013-10-28 13:11:31 -04:00
pidx = pic->plen++;
if (pidx >= pic->pcapa) {
pic_abort(pic, "constant pool overflow");
}
2013-10-28 13:11:31 -04:00
pic->pool[pidx] = pic_car(pic, pic_cdr(pic, obj));
irep->code[irep->clen].insn = OP_PUSHCONST;
irep->code[irep->clen].u.i = pidx;
irep->clen++;
break;
}
2013-10-28 13:11:31 -04:00
#define ARGC_ASSERT(n) do { \
if (pic_length(pic, obj) != (n) + 1) { \
pic_error(pic, "wrong number of arguments"); \
} \
} while (0)
else if (sym == pic->rCONS) {
2013-10-28 13:11:31 -04:00
ARGC_ASSERT(2);
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
codegen(state, pic_car(pic, pic_cdr(pic, pic_cdr(pic, obj))), false);
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_CONS;
irep->clen++;
break;
2013-10-23 14:14:32 -04:00
}
else if (sym == pic->rCAR) {
2013-10-28 13:11:31 -04:00
ARGC_ASSERT(1);
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_CAR;
2013-10-23 14:14:32 -04:00
irep->clen++;
break;
2013-10-28 13:11:31 -04:00
}
else if (sym == pic->rCDR) {
2013-10-28 13:11:31 -04:00
ARGC_ASSERT(1);
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_CDR;
2013-10-23 14:14:32 -04:00
irep->clen++;
break;
}
else if (sym == pic->rNILP) {
2013-10-28 13:11:31 -04:00
ARGC_ASSERT(1);
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_NILP;
irep->clen++;
break;
}
2013-11-06 04:55:06 -05:00
#define ARGC_ASSERT_GE(n) do { \
if (pic_length(pic, obj) < (n) + 1) { \
pic_error(pic, "wrong number of arguments"); \
} \
} while (0)
else if (sym == pic->rADD) {
2013-11-06 04:55:06 -05:00
pic_value args;
ARGC_ASSERT_GE(0);
switch (pic_length(pic, obj)) {
case 1:
irep->code[irep->clen].insn = OP_PUSHINT;
irep->code[irep->clen].u.i = 0;
irep->clen++;
break;
case 2:
codegen(state, pic_car(pic, pic_cdr(pic, obj)), tailpos);
break;
default:
args = pic_cdr(pic, obj);
codegen(state, pic_car(pic, args), false);
while (pic_length(pic, args) >= 2) {
codegen(state, pic_car(pic, pic_cdr(pic, args)), false);
irep->code[irep->clen].insn = OP_ADD;
irep->clen++;
args = pic_cdr(pic, args);
}
break;
}
2013-10-28 13:11:31 -04:00
break;
}
else if (sym == pic->rSUB) {
2013-11-06 22:18:00 -05:00
pic_value args;
ARGC_ASSERT_GE(1);
switch (pic_length(pic, obj)) {
case 2:
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
irep->code[irep->clen].insn = OP_MINUS;
irep->clen++;
break;
default:
args = pic_cdr(pic, obj);
codegen(state, pic_car(pic, args), false);
while (pic_length(pic, args) >= 2) {
codegen(state, pic_car(pic, pic_cdr(pic, args)), false);
irep->code[irep->clen].insn = OP_SUB;
irep->clen++;
args = pic_cdr(pic, args);
}
break;
}
2013-10-28 13:11:31 -04:00
break;
}
else if (sym == pic->rMUL) {
2013-11-06 22:52:59 -05:00
pic_value args;
ARGC_ASSERT_GE(0);
switch (pic_length(pic, obj)) {
case 1:
irep->code[irep->clen].insn = OP_PUSHINT;
irep->code[irep->clen].u.i = 1;
irep->clen++;
break;
case 2:
codegen(state, pic_car(pic, pic_cdr(pic, obj)), tailpos);
break;
default:
args = pic_cdr(pic, obj);
codegen(state, pic_car(pic, args), false);
while (pic_length(pic, args) >= 2) {
codegen(state, pic_car(pic, pic_cdr(pic, args)), false);
irep->code[irep->clen].insn = OP_MUL;
irep->clen++;
args = pic_cdr(pic, args);
}
break;
}
2013-10-28 13:11:31 -04:00
break;
}
else if (sym == pic->rDIV) {
2013-11-06 22:52:59 -05:00
pic_value args;
ARGC_ASSERT_GE(1);
switch (pic_length(pic, obj)) {
case 2:
irep->code[irep->clen].insn = OP_PUSHINT;
irep->code[irep->clen].u.i = 1;
irep->clen++;
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
irep->code[irep->clen].insn = OP_DIV;
irep->clen++;
break;
default:
args = pic_cdr(pic, obj);
codegen(state, pic_car(pic, args), false);
while (pic_length(pic, args) >= 2) {
codegen(state, pic_car(pic, pic_cdr(pic, args)), false);
irep->code[irep->clen].insn = OP_DIV;
irep->clen++;
args = pic_cdr(pic, args);
}
break;
}
2013-10-28 13:11:31 -04:00
break;
}
else if (sym == pic->rEQ) {
2013-10-28 13:11:31 -04:00
ARGC_ASSERT(2);
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
codegen(state, pic_car(pic, pic_cdr(pic, pic_cdr(pic, obj))), false);
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_EQ;
irep->clen++;
break;
}
else if (sym == pic->rLT) {
2013-10-28 13:11:31 -04:00
ARGC_ASSERT(2);
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
codegen(state, pic_car(pic, pic_cdr(pic, pic_cdr(pic, obj))), false);
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_LT;
irep->clen++;
break;
}
else if (sym == pic->rLE) {
2013-10-28 13:11:31 -04:00
ARGC_ASSERT(2);
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
codegen(state, pic_car(pic, pic_cdr(pic, pic_cdr(pic, obj))), false);
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_LE;
irep->clen++;
break;
}
else if (sym == pic->rGT) {
2013-10-28 13:11:31 -04:00
ARGC_ASSERT(2);
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, pic_cdr(pic, obj))), false);
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_LT;
irep->clen++;
break;
}
else if (sym == pic->rGE) {
2013-10-28 13:11:31 -04:00
ARGC_ASSERT(2);
2013-10-29 21:04:23 -04:00
codegen(state, pic_car(pic, pic_cdr(pic, pic_cdr(pic, obj))), false);
codegen(state, pic_car(pic, pic_cdr(pic, obj)), false);
2013-10-28 13:11:31 -04:00
irep->code[irep->clen].insn = OP_LE;
irep->clen++;
break;
}
2013-10-20 20:29:56 -04:00
}
2013-10-29 21:04:23 -04:00
codegen_call(state, obj, tailpos);
2013-10-28 13:11:31 -04:00
break;
2013-10-20 04:06:47 -04:00
}
case PIC_TT_BOOL: {
if (pic_true_p(obj)) {
irep->code[irep->clen].insn = OP_PUSHTRUE;
}
else {
irep->code[irep->clen].insn = OP_PUSHFALSE;
}
irep->clen++;
break;
}
case PIC_TT_FLOAT: {
2013-10-27 11:21:24 -04:00
irep->code[irep->clen].insn = OP_PUSHFLOAT;
2013-10-20 04:06:47 -04:00
irep->code[irep->clen].u.f = pic_float(obj);
irep->clen++;
break;
}
2013-10-27 11:21:24 -04:00
case PIC_TT_INT: {
irep->code[irep->clen].insn = OP_PUSHINT;
irep->code[irep->clen].u.i = pic_int(obj);
irep->clen++;
break;
}
2013-10-20 04:06:47 -04:00
case PIC_TT_NIL: {
irep->code[irep->clen].insn = OP_PUSHNIL;
irep->clen++;
break;
}
2013-11-04 21:37:18 -05:00
case PIC_TT_CHAR: {
irep->code[irep->clen].insn = OP_PUSHCHAR;
irep->code[irep->clen].u.c = pic_char(obj);
irep->clen++;
break;
}
2013-10-29 02:51:37 -04:00
case PIC_TT_STRING:
2013-11-04 22:38:23 -05:00
case PIC_TT_VECTOR:
case PIC_TT_BLOB: {
2013-10-20 21:48:03 -04:00
int pidx;
pidx = pic->plen++;
if (pidx >= pic->pcapa) {
pic_abort(pic, "constant pool overflow");
}
2013-10-20 21:48:03 -04:00
pic->pool[pidx] = obj;
irep->code[irep->clen].insn = OP_PUSHCONST;
irep->code[irep->clen].u.i = pidx;
irep->clen++;
break;
}
2013-11-09 00:14:25 -05:00
case PIC_TT_CONT:
2013-10-23 13:02:07 -04:00
case PIC_TT_ENV:
2013-10-20 04:06:47 -04:00
case PIC_TT_PROC:
case PIC_TT_UNDEF:
2013-10-22 03:02:20 -04:00
case PIC_TT_EOF:
2013-11-17 03:25:26 -05:00
case PIC_TT_PORT:
2013-11-26 07:05:02 -05:00
case PIC_TT_ERROR:
case PIC_TT_SENV:
case PIC_TT_SYNTAX:
2013-12-07 06:58:18 -05:00
case PIC_TT_SC:
case PIC_TT_LIB:
2014-01-18 02:51:54 -05:00
case PIC_TT_VAR:
case PIC_TT_IREP:
pic_error(pic, "invalid expression given");
2013-10-20 04:06:47 -04:00
}
}
static void
2013-10-29 21:04:23 -04:00
codegen_call(codegen_state *state, pic_value obj, bool tailpos)
2013-10-20 04:06:47 -04:00
{
2013-10-23 04:25:39 -04:00
pic_state *pic = state->pic;
struct pic_irep *irep = state->irep;
2013-10-20 04:06:47 -04:00
pic_value seq;
int i = 0;
for (seq = obj; ! pic_nil_p(seq); seq = pic_cdr(pic, seq)) {
2013-10-20 04:06:47 -04:00
pic_value v;
v = pic_car(pic, seq);
2013-10-29 21:04:23 -04:00
codegen(state, v, false);
2013-10-20 04:06:47 -04:00
++i;
}
2013-10-29 21:04:23 -04:00
irep->code[irep->clen].insn = tailpos ? OP_TAILCALL : OP_CALL;
2013-10-20 04:06:47 -04:00
irep->code[irep->clen].u.i = i;
irep->clen++;
}
static bool
valid_formal(pic_state *pic, pic_value formal)
{
if (pic_symbol_p(formal))
return true;
2013-10-24 11:35:37 -04:00
while (pic_pair_p(formal)) {
if (! pic_symbol_p(pic_car(pic, formal))) {
return false;
}
formal = pic_cdr(pic, formal);
}
if (pic_nil_p(formal))
return true;
if (pic_symbol_p(formal))
return true;
return false;
}
static void
lift_cv(pic_state *pic, struct pic_irep *irep, int d)
{
int i;
struct pic_code c;
for (i = 0; i < irep->clen; ++i) {
c = irep->code[i];
switch (c.insn) {
default:
/* pass */
break;
case OP_LAMBDA:
2013-11-28 21:41:45 -05:00
if (pic->irep[c.u.i]->cv_num == 0)
lift_cv(pic, pic->irep[c.u.i], d);
else
lift_cv(pic, pic->irep[c.u.i], d + 1);
break;
case OP_CREF:
case OP_CSET:
if (irep->code[i].u.r.depth > d)
irep->code[i].u.r.depth--;
break;
}
}
}
static void
slide_cv(pic_state *pic, unsigned *cv_tbl, unsigned cv_num, struct pic_irep *irep, int d)
{
int i, j;
struct pic_code c;
for (i = 0; i < irep->clen; ++i) {
c = irep->code[i];
switch (c.insn) {
default:
/* pass */
break;
case OP_LAMBDA:
if (pic->irep[c.u.i]->cv_num == 0) {
slide_cv(pic, cv_tbl, cv_num, pic->irep[c.u.i], d);
}
else {
slide_cv(pic, cv_tbl, cv_num, pic->irep[c.u.i], d + 1);
}
break;
case OP_CREF:
case OP_CSET:
2013-11-04 21:32:09 -05:00
if (d != c.u.r.depth)
break;
for (j = 0; j < cv_num; ++j) {
2013-11-04 21:32:09 -05:00
if (c.u.r.idx == cv_tbl[j]) {
irep->code[i].u.r.idx = j;
break;
}
}
break;
}
}
}
2013-10-20 04:06:47 -04:00
static struct pic_irep *
2013-10-23 04:26:02 -04:00
codegen_lambda(codegen_state *state, pic_value obj)
2013-10-20 04:06:47 -04:00
{
2013-10-23 04:25:39 -04:00
pic_state *pic = state->pic;
codegen_scope *prev_scope;
struct pic_irep *prev_irep, *irep;
pic_value args, body, v;
int i, c, k;
2013-10-23 04:25:39 -04:00
if (pic_length(pic, obj) < 2) {
pic_error(pic, "syntax error");
}
args = pic_car(pic, pic_cdr(pic, obj));
if (! valid_formal(pic, args)) {
pic_error(pic, "syntax error");
}
2013-10-23 04:25:39 -04:00
/* inner environment */
prev_irep = state->irep;
prev_scope = state->scope;
state->scope = new_local_scope(pic, args, state->scope);
state->irep = irep = new_irep(pic);
irep->argc = state->scope->argc;
2013-10-27 05:13:36 -04:00
irep->varg = state->scope->varg;
2013-10-23 04:25:39 -04:00
{
/* body */
body = pic_cdr(pic, pic_cdr(pic, obj));
for (v = body; ! pic_nil_p(v); v = pic_cdr(pic, v)) {
2013-10-29 21:04:23 -04:00
if (pic_nil_p(pic_cdr(pic, v))) {
codegen(state, pic_car(pic, v), true);
}
else {
codegen(state, pic_car(pic, v), false);
irep->code[irep->clen].insn = OP_POP;
irep->clen++;
}
2013-10-23 04:25:39 -04:00
}
irep->code[irep->clen].insn = OP_RET;
2013-10-20 04:06:47 -04:00
irep->clen++;
2013-11-13 04:08:22 -05:00
irep->localc = state->scope->localc;
/* fixup local references */
2013-10-23 13:51:02 -04:00
for (i = 0; i < irep->clen; ++i) {
struct pic_code c = irep->code[i];
2013-11-04 19:14:21 -05:00
switch (c.insn) {
default:
/* pass */
break;
case OP_CREF:
2013-11-04 21:32:09 -05:00
if (c.u.r.depth == 0 && ! state->scope->dirty_flags[c.u.r.idx]) {
2013-11-04 19:14:21 -05:00
irep->code[i].insn = OP_LREF;
2013-11-04 21:32:09 -05:00
irep->code[i].u.i = irep->code[i].u.r.idx;
2013-11-04 19:14:21 -05:00
}
break;
case OP_CSET:
2013-11-04 21:32:09 -05:00
if (c.u.r.depth == 0 && ! state->scope->dirty_flags[c.u.r.idx]) {
2013-11-04 19:14:21 -05:00
irep->code[i].insn = OP_LSET;
2013-11-04 21:32:09 -05:00
irep->code[i].u.i = irep->code[i].u.r.idx;
2013-11-04 19:14:21 -05:00
}
break;
2013-10-23 13:51:02 -04:00
}
}
/* fixup closed variables */
c = 0;
for (i = 0; i < irep->argc + irep->localc; ++i) {
if (state->scope->dirty_flags[i])
++c;
}
if (c == 0) {
lift_cv(pic, irep, 0);
irep->cv_tbl = NULL;
irep->cv_num = 0;
}
else {
irep->cv_tbl = (unsigned *)pic_calloc(pic, c, sizeof(unsigned));
k = 0;
for (i = 0; i < irep->argc + irep->localc; ++i) {
if (state->scope->dirty_flags[i]) {
irep->cv_tbl[k] = i;
++k;
}
2013-10-23 14:14:32 -04:00
}
irep->cv_num = c;
slide_cv(pic, irep->cv_tbl, irep->cv_num, irep, 0);
}
2013-10-20 04:06:47 -04:00
}
2013-10-23 04:25:39 -04:00
destroy_scope(pic, state->scope);
2013-10-23 04:25:39 -04:00
state->irep = prev_irep;
state->scope = prev_scope;
2013-10-20 04:06:47 -04:00
#if VM_DEBUG
2013-11-16 09:32:34 -05:00
printf("* generated lambda:\n");
2014-01-18 02:23:12 -05:00
pic_dump_irep(pic, irep);
2013-10-20 04:06:47 -04:00
puts("");
#endif
return irep;
}
struct pic_proc *
pic_codegen(pic_state *pic, pic_value obj)
2013-10-20 04:06:47 -04:00
{
struct pic_proc *proc;
2013-10-23 04:25:39 -04:00
codegen_state *state;
jmp_buf jmp, *prev_jmp = pic->jmp;
2013-11-15 05:54:47 -05:00
int ai = pic_gc_arena_preserve(pic);
2013-10-20 04:06:47 -04:00
2013-10-23 04:25:39 -04:00
state = new_codegen_state(pic);
2013-10-20 04:06:47 -04:00
if (setjmp(jmp) == 0) {
pic->jmp = &jmp;
}
else {
/* error occured */
proc = NULL;
goto exit;
}
state->irep = new_irep(pic);
state->irep->argc = 1;
state->irep->localc = 0;
2013-11-18 02:36:44 -05:00
codegen(state, pic_macroexpand(pic, obj), false);
state->irep->code[state->irep->clen].insn = OP_RET;
2013-10-23 04:25:39 -04:00
state->irep->clen++;
state->irep->cv_num = 0;
state->irep->cv_tbl = NULL;
2014-01-08 08:44:53 -05:00
proc = pic_proc_new_irep(pic, state->irep, NULL);
2013-10-20 04:06:47 -04:00
2013-10-23 04:25:39 -04:00
destroy_codegen_state(pic, state);
2013-10-20 04:06:47 -04:00
#if VM_DEBUG
2014-01-18 02:23:12 -05:00
pic_dump_irep(pic, proc->u.irep);
2013-10-20 04:06:47 -04:00
#endif
exit:
pic->jmp = prev_jmp;
2013-11-15 05:54:47 -05:00
pic_gc_arena_restore(pic, ai);
pic_gc_protect(pic, pic_obj_value(proc));
2013-10-20 04:06:47 -04:00
return proc;
}
2013-10-23 02:52:14 -04:00
void
pic_define(pic_state *pic, const char *name, pic_value val)
2013-10-23 02:52:14 -04:00
{
int idx;
pic_sym gsym;
gsym = pic_gensym(pic, pic_intern_cstr(pic, name));
/* push to the global arena */
idx = scope_global_define(pic, pic_symbol_name(pic, gsym));
pic->globals[idx] = val;
2013-10-23 02:52:14 -04:00
/* register to the senv */
xh_put(pic->lib->senv->tbl, name, gsym);
/* export! */
pic_export(pic, pic_intern_cstr(pic, name));
2013-10-23 02:52:14 -04:00
}
2014-01-17 22:55:44 -05:00
static int
global_ref(pic_state *pic, const char *name)
2014-01-12 10:50:45 -05:00
{
struct xh_entry *e;
if (! (e = xh_get(pic->lib->senv->tbl, name))) {
pic_error(pic, "symbol not defined");
}
assert(e->val >= 0);
if (! (e = xh_get(pic->global_tbl, pic_symbol_name(pic, (pic_sym)e->val)))) {
pic_abort(pic, "logic flaw");
}
2014-01-17 22:55:44 -05:00
return e->val;
}
pic_value
pic_ref(pic_state *pic, const char *name)
{
int gid;
gid = global_ref(pic, name);
return pic->globals[gid];
}
void
pic_set(pic_state *pic, const char *name, pic_value value)
{
int gid;
gid = global_ref(pic, name);
pic->globals[gid] = value;
2014-01-12 10:50:45 -05:00
}
2013-10-30 03:43:15 -04:00
void
2013-12-10 04:47:45 -05:00
print_code(pic_state *pic, struct pic_code c)
2013-10-23 02:52:14 -04:00
{
2013-12-10 04:47:45 -05:00
printf("[%2d] ", c.insn);
switch (c.insn) {
2013-10-23 02:52:14 -04:00
case OP_POP:
puts("OP_POP");
break;
case OP_PUSHNIL:
puts("OP_PUSHNIL");
break;
case OP_PUSHTRUE:
puts("OP_PUSHTRUE");
break;
case OP_PUSHFALSE:
puts("OP_PUSHFALSE");
break;
2013-10-27 11:21:24 -04:00
case OP_PUSHFLOAT:
2013-12-10 04:47:45 -05:00
printf("OP_PUSHFLOAT\t%f\n", c.u.f);
2013-10-27 11:21:24 -04:00
break;
case OP_PUSHINT:
2013-12-10 04:47:45 -05:00
printf("OP_PUSHINT\t%d\n", c.u.i);
2013-10-23 02:52:14 -04:00
break;
2013-11-04 21:37:18 -05:00
case OP_PUSHCHAR:
2013-12-10 04:47:45 -05:00
printf("OP_PUSHCHAR\t%c\n", c.u.c);
2013-11-04 21:37:18 -05:00
break;
2013-10-23 02:52:14 -04:00
case OP_PUSHCONST:
printf("OP_PUSHCONST\t");
2013-12-10 04:47:45 -05:00
pic_debug(pic, pic->pool[c.u.i]);
2013-10-23 02:52:14 -04:00
puts("");
break;
case OP_GREF:
2013-12-10 04:47:45 -05:00
printf("OP_GREF\t%i\n", c.u.i);
2013-10-23 02:52:14 -04:00
break;
case OP_GSET:
2013-12-10 04:47:45 -05:00
printf("OP_GSET\t%i\n", c.u.i);
2013-10-23 02:52:14 -04:00
break;
case OP_LREF:
2013-12-10 04:47:45 -05:00
printf("OP_LREF\t%d\n", c.u.i);
2013-10-23 02:52:14 -04:00
break;
2013-10-23 14:14:32 -04:00
case OP_LSET:
2013-12-10 04:47:45 -05:00
printf("OP_LSET\t%d\n", c.u.i);
2013-10-23 14:14:32 -04:00
break;
2013-10-23 13:04:49 -04:00
case OP_CREF:
2013-12-10 04:47:45 -05:00
printf("OP_CREF\t%d\t%d\n", c.u.r.depth, c.u.r.idx);
2013-10-23 13:04:49 -04:00
break;
2013-10-23 14:14:32 -04:00
case OP_CSET:
2013-12-10 04:47:45 -05:00
printf("OP_CSET\t%d\t%d\n", c.u.r.depth, c.u.r.idx);
2013-10-23 14:14:32 -04:00
break;
2013-10-23 02:52:14 -04:00
case OP_JMP:
2013-12-10 04:47:45 -05:00
printf("OP_JMP\t%d\n", c.u.i);
2013-10-23 02:52:14 -04:00
break;
case OP_JMPIF:
2013-12-10 04:47:45 -05:00
printf("OP_JMPIF\t%d\n", c.u.i);
2013-10-23 02:52:14 -04:00
break;
case OP_CALL:
2013-12-10 04:47:45 -05:00
printf("OP_CALL\t%d\n", c.u.i);
2013-10-23 02:52:14 -04:00
break;
2013-10-29 21:03:46 -04:00
case OP_TAILCALL:
2013-12-10 04:47:45 -05:00
printf("OP_TAILCALL\t%d\n", c.u.i);
2013-10-29 21:03:46 -04:00
break;
2013-10-23 02:52:14 -04:00
case OP_RET:
puts("OP_RET");
break;
case OP_LAMBDA:
2013-12-10 04:47:45 -05:00
printf("OP_LAMBDA\t%d\n", c.u.i);
2013-10-23 02:52:14 -04:00
break;
case OP_CONS:
puts("OP_CONS");
break;
case OP_CAR:
puts("OP_CAR");
break;
case OP_NILP:
puts("OP_NILP");
break;
case OP_CDR:
puts("OP_CDR");
break;
case OP_ADD:
puts("OP_ADD");
break;
case OP_SUB:
puts("OP_SUB");
break;
case OP_MUL:
puts("OP_MUL");
break;
case OP_DIV:
puts("OP_DIV");
break;
2013-11-06 22:18:00 -05:00
case OP_MINUS:
puts("OP_MINUS");
break;
2013-10-24 08:10:13 -04:00
case OP_EQ:
puts("OP_EQ");
break;
case OP_LT:
puts("OP_LT");
break;
case OP_LE:
puts("OP_LE");
break;
2013-10-23 02:52:14 -04:00
case OP_STOP:
puts("OP_STOP");
break;
}
2013-12-10 04:47:45 -05:00
}
void
2014-01-18 02:23:12 -05:00
pic_dump_irep(pic_state *pic, struct pic_irep *irep)
2013-12-10 04:47:45 -05:00
{
int i;
printf("## irep %p\n", (void *)irep);
2013-12-10 04:47:45 -05:00
printf("[clen = %zd, ccapa = %zd, argc = %d, localc = %d]\n", irep->clen, irep->ccapa, irep->argc, irep->localc);
printf(":: cv_num = %d\n", irep->cv_num);
for (i = 0; i < irep->cv_num; ++i) {
printf(": %d -> %d\n", irep->cv_tbl[i], i);
}
for (i = 0; i < irep->clen; ++i) {
print_code(pic, irep->code[i]);
2013-10-23 02:52:14 -04:00
}
}