1505 lines
38 KiB
C
1505 lines
38 KiB
C
/**
|
|
* See Copyright Notice in picrin.h
|
|
*/
|
|
|
|
#include "picrin.h"
|
|
#include "picrin/pair.h"
|
|
#include "picrin/irep.h"
|
|
#include "picrin/proc.h"
|
|
#include "picrin/lib.h"
|
|
#include "picrin/macro.h"
|
|
|
|
#if PIC_NONE_IS_FALSE
|
|
# define OP_PUSHNONE OP_PUSHFALSE
|
|
#else
|
|
# error enable PIC_NONE_IS_FALSE
|
|
#endif
|
|
|
|
/**
|
|
* scope object
|
|
*/
|
|
|
|
typedef struct analyze_scope {
|
|
int depth;
|
|
bool varg;
|
|
xvect args, locals, captures; /* rest args variable is counted as a local */
|
|
pic_value defer;
|
|
struct analyze_scope *up;
|
|
} analyze_scope;
|
|
|
|
/**
|
|
* global analyzer state
|
|
*/
|
|
|
|
typedef struct analyze_state {
|
|
pic_state *pic;
|
|
analyze_scope *scope;
|
|
pic_sym rCONS, rCAR, rCDR, rNILP;
|
|
pic_sym rADD, rSUB, rMUL, rDIV;
|
|
pic_sym rEQ, rLT, rLE, rGT, rGE, rNOT;
|
|
pic_sym rVALUES, rCALL_WITH_VALUES;
|
|
pic_sym sCALL, sTAILCALL, sCALL_WITH_VALUES, sTAILCALL_WITH_VALUES;
|
|
pic_sym sGREF, sLREF, sCREF, sRETURN;
|
|
} analyze_state;
|
|
|
|
static bool push_scope(analyze_state *, pic_value);
|
|
static void pop_scope(analyze_state *);
|
|
|
|
#define register_symbol(pic, state, slot, name) do { \
|
|
state->slot = pic_intern_cstr(pic, name); \
|
|
} while (0)
|
|
|
|
#define register_renamed_symbol(pic, state, slot, lib, id) do { \
|
|
pic_sym sym, gsym; \
|
|
sym = pic_intern_cstr(pic, id); \
|
|
if (! pic_find_rename(pic, lib->env, sym, &gsym)) { \
|
|
pic_errorf(pic, "internal error! native VM procedure not found: %s", id); \
|
|
} \
|
|
state->slot = gsym; \
|
|
} while (0)
|
|
|
|
static analyze_state *
|
|
new_analyze_state(pic_state *pic)
|
|
{
|
|
analyze_state *state;
|
|
xh_entry *it;
|
|
|
|
state = pic_alloc(pic, sizeof(analyze_state));
|
|
state->pic = pic;
|
|
state->scope = NULL;
|
|
|
|
/* native VM procedures */
|
|
register_renamed_symbol(pic, state, rCONS, pic->PICRIN_BASE, "cons");
|
|
register_renamed_symbol(pic, state, rCAR, pic->PICRIN_BASE, "car");
|
|
register_renamed_symbol(pic, state, rCDR, pic->PICRIN_BASE, "cdr");
|
|
register_renamed_symbol(pic, state, rNILP, pic->PICRIN_BASE, "null?");
|
|
register_renamed_symbol(pic, state, rADD, pic->PICRIN_BASE, "+");
|
|
register_renamed_symbol(pic, state, rSUB, pic->PICRIN_BASE, "-");
|
|
register_renamed_symbol(pic, state, rMUL, pic->PICRIN_BASE, "*");
|
|
register_renamed_symbol(pic, state, rDIV, pic->PICRIN_BASE, "/");
|
|
register_renamed_symbol(pic, state, rEQ, pic->PICRIN_BASE, "=");
|
|
register_renamed_symbol(pic, state, rLT, pic->PICRIN_BASE, "<");
|
|
register_renamed_symbol(pic, state, rLE, pic->PICRIN_BASE, "<=");
|
|
register_renamed_symbol(pic, state, rGT, pic->PICRIN_BASE, ">");
|
|
register_renamed_symbol(pic, state, rGE, pic->PICRIN_BASE, ">=");
|
|
register_renamed_symbol(pic, state, rNOT, pic->PICRIN_BASE, "not");
|
|
register_renamed_symbol(pic, state, rVALUES, pic->PICRIN_BASE, "values");
|
|
register_renamed_symbol(pic, state, rCALL_WITH_VALUES, pic->PICRIN_BASE, "call-with-values");
|
|
|
|
register_symbol(pic, state, sCALL, "call");
|
|
register_symbol(pic, state, sTAILCALL, "tail-call");
|
|
register_symbol(pic, state, sCALL_WITH_VALUES, "call-with-values");
|
|
register_symbol(pic, state, sTAILCALL_WITH_VALUES, "tailcall-with-values");
|
|
register_symbol(pic, state, sGREF, "gref");
|
|
register_symbol(pic, state, sLREF, "lref");
|
|
register_symbol(pic, state, sCREF, "cref");
|
|
register_symbol(pic, state, sRETURN, "return");
|
|
|
|
/* push initial scope */
|
|
push_scope(state, pic_nil_value());
|
|
|
|
for (it = xh_begin(&pic->globals); it != NULL; it = xh_next(it)) {
|
|
pic_sym sym = xh_key(it, pic_sym);
|
|
xv_push(&state->scope->locals, &sym);
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
static void
|
|
destroy_analyze_state(analyze_state *state)
|
|
{
|
|
pop_scope(state);
|
|
pic_free(state->pic, state);
|
|
}
|
|
|
|
static bool
|
|
analyze_args(pic_state *pic, pic_value formals, bool *varg, xvect *args, xvect *locals)
|
|
{
|
|
pic_value v, t;
|
|
pic_sym sym;
|
|
|
|
for (v = formals; pic_pair_p(v); v = pic_cdr(pic, v)) {
|
|
t = pic_car(pic, v);
|
|
if (! pic_sym_p(t)) {
|
|
return false;
|
|
}
|
|
sym = pic_sym(t);
|
|
xv_push(args, &sym);
|
|
}
|
|
if (pic_nil_p(v)) {
|
|
*varg = false;
|
|
}
|
|
else if (pic_sym_p(v)) {
|
|
*varg = true;
|
|
sym = pic_sym(v);
|
|
xv_push(locals, &sym);
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
push_scope(analyze_state *state, pic_value formals)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
analyze_scope *scope;
|
|
bool varg;
|
|
xvect args, locals, captures;
|
|
|
|
xv_init(&args, sizeof(pic_sym));
|
|
xv_init(&locals, sizeof(pic_sym));
|
|
xv_init(&captures, sizeof(pic_sym));
|
|
|
|
if (analyze_args(pic, formals, &varg, &args, &locals)) {
|
|
scope = pic_alloc(pic, sizeof(analyze_scope));
|
|
scope->up = state->scope;
|
|
scope->depth = scope->up ? scope->up->depth + 1 : 0;
|
|
scope->varg = varg;
|
|
scope->args = args;
|
|
scope->locals = locals;
|
|
scope->captures = captures;
|
|
scope->defer = pic_nil_value();
|
|
|
|
state->scope = scope;
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
xv_destroy(&args);
|
|
xv_destroy(&locals);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pop_scope(analyze_state *state)
|
|
{
|
|
analyze_scope *scope;
|
|
|
|
scope = state->scope;
|
|
xv_destroy(&scope->args);
|
|
xv_destroy(&scope->locals);
|
|
xv_destroy(&scope->captures);
|
|
|
|
scope = scope->up;
|
|
pic_free(state->pic, state->scope);
|
|
state->scope = scope;
|
|
}
|
|
|
|
static bool
|
|
lookup_scope(analyze_scope *scope, pic_sym sym)
|
|
{
|
|
pic_sym *arg, *local;
|
|
size_t i;
|
|
|
|
/* args */
|
|
for (i = 0; i < xv_size(&scope->args); ++i) {
|
|
arg = xv_get(&scope->args, i);
|
|
if (*arg == sym)
|
|
return true;
|
|
}
|
|
/* locals */
|
|
for (i = 0; i < xv_size(&scope->locals); ++i) {
|
|
local = xv_get(&scope->locals, i);
|
|
if (*local == sym)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
capture_var(analyze_scope *scope, pic_sym sym)
|
|
{
|
|
pic_sym *var;
|
|
size_t i;
|
|
|
|
for (i = 0; i < xv_size(&scope->captures); ++i) {
|
|
var = xv_get(&scope->captures, i);
|
|
if (*var == sym) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == xv_size(&scope->captures)) {
|
|
xv_push(&scope->captures, &sym);
|
|
}
|
|
}
|
|
|
|
static int
|
|
find_var(analyze_state *state, pic_sym sym)
|
|
{
|
|
analyze_scope *scope = state->scope;
|
|
int depth = 0;
|
|
|
|
while (scope) {
|
|
if (lookup_scope(scope, sym)) {
|
|
if (depth > 0) {
|
|
capture_var(scope, sym);
|
|
}
|
|
return depth;
|
|
}
|
|
depth++;
|
|
scope = scope->up;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
define_var(analyze_state *state, pic_sym sym)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
analyze_scope *scope = state->scope;
|
|
|
|
if (lookup_scope(scope, sym)) {
|
|
pic_warnf(pic, "redefining variable: ~s", pic_sym_value(sym));
|
|
return;
|
|
}
|
|
|
|
xv_push(&scope->locals, &sym);
|
|
}
|
|
|
|
static pic_value analyze_node(analyze_state *, pic_value, bool);
|
|
static pic_value analyze_procedure(analyze_state *, pic_value, pic_value, pic_value);
|
|
|
|
static pic_value
|
|
analyze(analyze_state *state, pic_value obj, bool tailpos)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
size_t ai = pic_gc_arena_preserve(pic);
|
|
pic_value res;
|
|
pic_sym tag;
|
|
|
|
res = analyze_node(state, obj, tailpos);
|
|
|
|
tag = pic_sym(pic_car(pic, res));
|
|
if (tailpos) {
|
|
if (tag == pic->sIF || tag == pic->sBEGIN || tag == state->sTAILCALL || tag == state->sTAILCALL_WITH_VALUES || tag == state->sRETURN) {
|
|
/* pass through */
|
|
}
|
|
else {
|
|
res = pic_list2(pic, pic_symbol_value(state->sRETURN), res);
|
|
}
|
|
}
|
|
|
|
pic_gc_arena_restore(pic, ai);
|
|
pic_gc_protect(pic, res);
|
|
pic_gc_protect(pic, state->scope->defer);
|
|
return res;
|
|
}
|
|
|
|
static pic_value
|
|
analyze_global_var(analyze_state *state, pic_sym sym)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
|
|
return pic_list2(pic, pic_symbol_value(state->sGREF), pic_sym_value(sym));
|
|
}
|
|
|
|
static pic_value
|
|
analyze_local_var(analyze_state *state, pic_sym sym)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
|
|
return pic_list2(pic, pic_symbol_value(state->sLREF), pic_sym_value(sym));
|
|
}
|
|
|
|
static pic_value
|
|
analyze_free_var(analyze_state *state, pic_sym sym, int depth)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
|
|
return pic_list3(pic, pic_symbol_value(state->sCREF), pic_int_value(depth), pic_sym_value(sym));
|
|
}
|
|
|
|
static pic_value
|
|
analyze_var(analyze_state *state, pic_sym sym)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
int depth;
|
|
|
|
if ((depth = find_var(state, sym)) == -1) {
|
|
pic_errorf(pic, "unbound variable %s", pic_symbol_name(pic, sym));
|
|
}
|
|
|
|
if (depth == state->scope->depth) {
|
|
return analyze_global_var(state, sym);
|
|
} else if (depth == 0) {
|
|
return analyze_local_var(state, sym);
|
|
} else {
|
|
return analyze_free_var(state, sym, depth);
|
|
}
|
|
}
|
|
|
|
static pic_value
|
|
analyze_defer(analyze_state *state, pic_value name, pic_value formal, pic_value body)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
const pic_sym sNOWHERE = pic_intern_cstr(pic, " nowhere ");
|
|
pic_value skel;
|
|
|
|
skel = pic_list2(pic, pic_sym_value(state->sGREF), pic_sym_value(sNOWHERE));
|
|
|
|
pic_push(pic, pic_list4(pic, name, formal, body, skel), state->scope->defer);
|
|
|
|
return skel;
|
|
}
|
|
|
|
static void
|
|
analyze_deferred(analyze_state *state)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value defer, val, name, formal, body, dst;
|
|
|
|
pic_for_each (defer, pic_reverse(pic, state->scope->defer)) {
|
|
name = pic_list_ref(pic, defer, 0);
|
|
formal = pic_list_ref(pic, defer, 1);
|
|
body = pic_list_ref(pic, defer, 2);
|
|
dst = pic_list_ref(pic, defer, 3);
|
|
|
|
val = analyze_procedure(state, name, formal, body);
|
|
|
|
/* copy */
|
|
pic_pair_ptr(dst)->car = pic_car(pic, val);
|
|
pic_pair_ptr(dst)->cdr = pic_cdr(pic, val);
|
|
}
|
|
|
|
state->scope->defer = pic_nil_value();
|
|
}
|
|
|
|
static pic_value
|
|
analyze_procedure(analyze_state *state, pic_value name, pic_value formals, pic_value body_exprs)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value args, locals, varg, captures, body;
|
|
|
|
assert(pic_sym_p(name) || pic_false_p(name));
|
|
|
|
if (push_scope(state, formals)) {
|
|
analyze_scope *scope = state->scope;
|
|
pic_sym *var;
|
|
size_t i;
|
|
|
|
args = pic_nil_value();
|
|
for (i = xv_size(&scope->args); i > 0; --i) {
|
|
var = xv_get(&scope->args, i - 1);
|
|
pic_push(pic, pic_sym_value(*var), args);
|
|
}
|
|
|
|
varg = scope->varg
|
|
? pic_true_value()
|
|
: pic_false_value();
|
|
|
|
/* To know what kind of local variables are defined, analyze body at first. */
|
|
body = analyze(state, pic_cons(pic, pic_sym_value(pic->rBEGIN), body_exprs), true);
|
|
|
|
analyze_deferred(state);
|
|
|
|
locals = pic_nil_value();
|
|
for (i = xv_size(&scope->locals); i > 0; --i) {
|
|
var = xv_get(&scope->locals, i - 1);
|
|
pic_push(pic, pic_sym_value(*var), locals);
|
|
}
|
|
|
|
captures = pic_nil_value();
|
|
for (i = xv_size(&scope->captures); i > 0; --i) {
|
|
var = xv_get(&scope->captures, i - 1);
|
|
pic_push(pic, pic_sym_value(*var), captures);
|
|
}
|
|
|
|
pop_scope(state);
|
|
}
|
|
else {
|
|
pic_errorf(pic, "invalid formal syntax: ~s", args);
|
|
}
|
|
|
|
return pic_list7(pic, pic_sym_value(pic->sLAMBDA), name, args, locals, varg, captures, body);
|
|
}
|
|
|
|
static pic_value
|
|
analyze_lambda(analyze_state *state, pic_value obj)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value formals, body_exprs;
|
|
|
|
if (pic_length(pic, obj) < 2) {
|
|
pic_errorf(pic, "syntax error");
|
|
}
|
|
|
|
formals = pic_list_ref(pic, obj, 1);
|
|
body_exprs = pic_list_tail(pic, obj, 2);
|
|
|
|
return analyze_defer(state, pic_false_value(), formals, body_exprs);
|
|
}
|
|
|
|
static pic_value
|
|
analyze_declare(analyze_state *state, pic_sym var)
|
|
{
|
|
define_var(state, var);
|
|
|
|
return analyze_var(state, var);
|
|
}
|
|
|
|
static pic_value
|
|
analyze_define(analyze_state *state, pic_value obj)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value var, val;
|
|
pic_sym sym;
|
|
|
|
if (pic_length(pic, obj) != 3) {
|
|
pic_errorf(pic, "syntax error");
|
|
}
|
|
|
|
var = pic_list_ref(pic, obj, 1);
|
|
if (! pic_sym_p(var)) {
|
|
pic_errorf(pic, "syntax error");
|
|
} else {
|
|
sym = pic_sym(var);
|
|
}
|
|
var = analyze_declare(state, sym);
|
|
|
|
if (pic_pair_p(pic_list_ref(pic, obj, 2))
|
|
&& pic_sym_p(pic_list_ref(pic, pic_list_ref(pic, obj, 2), 0))
|
|
&& pic_sym(pic_list_ref(pic, pic_list_ref(pic, obj, 2), 0)) == pic->rLAMBDA) {
|
|
pic_value formals, body_exprs;
|
|
|
|
formals = pic_list_ref(pic, pic_list_ref(pic, obj, 2), 1);
|
|
body_exprs = pic_list_tail(pic, pic_list_ref(pic, obj, 2), 2);
|
|
|
|
val = analyze_defer(state, pic_sym_value(sym), formals, body_exprs);
|
|
} else {
|
|
if (pic_length(pic, obj) != 3) {
|
|
pic_errorf(pic, "syntax error");
|
|
}
|
|
val = analyze(state, pic_list_ref(pic, obj, 2), false);
|
|
}
|
|
|
|
return pic_list3(pic, pic_symbol_value(pic->sSETBANG), var, val);
|
|
}
|
|
|
|
static pic_value
|
|
analyze_if(analyze_state *state, pic_value obj, bool tailpos)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value cond, if_true, if_false;
|
|
|
|
if_false = pic_none_value();
|
|
switch (pic_length(pic, obj)) {
|
|
default:
|
|
pic_errorf(pic, "syntax error");
|
|
break;
|
|
case 4:
|
|
if_false = pic_list_ref(pic, obj, 3);
|
|
FALLTHROUGH;
|
|
case 3:
|
|
if_true = pic_list_ref(pic, obj, 2);
|
|
}
|
|
|
|
/* analyze in order */
|
|
cond = analyze(state, pic_list_ref(pic, obj, 1), false);
|
|
if_true = analyze(state, if_true, tailpos);
|
|
if_false = analyze(state, if_false, tailpos);
|
|
|
|
return pic_list4(pic, pic_symbol_value(pic->sIF), cond, if_true, if_false);
|
|
}
|
|
|
|
static pic_value
|
|
analyze_begin(analyze_state *state, pic_value obj, bool tailpos)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value seq;
|
|
bool tail;
|
|
|
|
switch (pic_length(pic, obj)) {
|
|
case 1:
|
|
return analyze(state, pic_none_value(), tailpos);
|
|
case 2:
|
|
return analyze(state, pic_list_ref(pic, obj, 1), tailpos);
|
|
default:
|
|
seq = pic_list1(pic, pic_symbol_value(pic->sBEGIN));
|
|
for (obj = pic_cdr(pic, obj); ! pic_nil_p(obj); obj = pic_cdr(pic, obj)) {
|
|
if (pic_nil_p(pic_cdr(pic, obj))) {
|
|
tail = tailpos;
|
|
} else {
|
|
tail = false;
|
|
}
|
|
seq = pic_cons(pic, analyze(state, pic_car(pic, obj), tail), seq);
|
|
}
|
|
return pic_reverse(pic, seq);
|
|
}
|
|
}
|
|
|
|
static pic_value
|
|
analyze_set(analyze_state *state, pic_value obj)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value var, val;
|
|
|
|
if (pic_length(pic, obj) != 3) {
|
|
pic_errorf(pic, "syntax error");
|
|
}
|
|
|
|
var = pic_list_ref(pic, obj, 1);
|
|
if (! pic_sym_p(var)) {
|
|
pic_errorf(pic, "syntax error");
|
|
}
|
|
|
|
val = pic_list_ref(pic, obj, 2);
|
|
|
|
var = analyze(state, var, false);
|
|
val = analyze(state, val, false);
|
|
|
|
return pic_list3(pic, pic_symbol_value(pic->sSETBANG), var, val);
|
|
}
|
|
|
|
static pic_value
|
|
analyze_quote(analyze_state *state, pic_value obj)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
|
|
if (pic_length(pic, obj) != 2) {
|
|
pic_errorf(pic, "syntax error");
|
|
}
|
|
return pic_list2(pic, pic_sym_value(pic->sQUOTE), pic_list_ref(pic, obj, 1));
|
|
}
|
|
|
|
#define ARGC_ASSERT_GE(n) do { \
|
|
if (pic_length(pic, obj) < (n) + 1) { \
|
|
pic_errorf(pic, "wrong number of arguments"); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define FOLD_ARGS(sym) do { \
|
|
obj = analyze(state, pic_car(pic, args), false); \
|
|
pic_for_each (arg, pic_cdr(pic, args)) { \
|
|
obj = pic_list3(pic, pic_symbol_value(sym), obj, \
|
|
analyze(state, arg, false)); \
|
|
} \
|
|
} while (0)
|
|
|
|
static pic_value
|
|
analyze_add(analyze_state *state, pic_value obj, bool tailpos)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value args, arg;
|
|
|
|
ARGC_ASSERT_GE(0);
|
|
switch (pic_length(pic, obj)) {
|
|
case 1:
|
|
return pic_list2(pic, pic_symbol_value(pic->sQUOTE), pic_int_value(0));
|
|
case 2:
|
|
return analyze(state, pic_car(pic, pic_cdr(pic, obj)), tailpos);
|
|
default:
|
|
args = pic_cdr(pic, obj);
|
|
FOLD_ARGS(pic->sADD);
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
static pic_value
|
|
analyze_sub(analyze_state *state, pic_value obj)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value args, arg;
|
|
|
|
ARGC_ASSERT_GE(1);
|
|
switch (pic_length(pic, obj)) {
|
|
case 2:
|
|
return pic_list2(pic, pic_symbol_value(pic->sMINUS),
|
|
analyze(state, pic_car(pic, pic_cdr(pic, obj)), false));
|
|
default:
|
|
args = pic_cdr(pic, obj);
|
|
FOLD_ARGS(pic->sSUB);
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
static pic_value
|
|
analyze_mul(analyze_state *state, pic_value obj, bool tailpos)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value args, arg;
|
|
|
|
ARGC_ASSERT_GE(0);
|
|
switch (pic_length(pic, obj)) {
|
|
case 1:
|
|
return pic_list2(pic, pic_symbol_value(pic->sQUOTE), pic_int_value(1));
|
|
case 2:
|
|
return analyze(state, pic_car(pic, pic_cdr(pic, obj)), tailpos);
|
|
default:
|
|
args = pic_cdr(pic, obj);
|
|
FOLD_ARGS(pic->sMUL);
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
static pic_value
|
|
analyze_div(analyze_state *state, pic_value obj)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value args, arg;
|
|
|
|
ARGC_ASSERT_GE(1);
|
|
switch (pic_length(pic, obj)) {
|
|
case 2:
|
|
args = pic_cdr(pic, obj);
|
|
obj = pic_list3(pic, pic_car(pic, obj), pic_float_value(1), pic_car(pic, args));
|
|
return analyze(state, obj, false);
|
|
default:
|
|
args = pic_cdr(pic, obj);
|
|
FOLD_ARGS(pic->sDIV);
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
static pic_value
|
|
analyze_call(analyze_state *state, pic_value obj, bool tailpos)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value seq, elt;
|
|
pic_sym call;
|
|
|
|
if (! tailpos) {
|
|
call = state->sCALL;
|
|
} else {
|
|
call = state->sTAILCALL;
|
|
}
|
|
seq = pic_list1(pic, pic_symbol_value(call));
|
|
pic_for_each (elt, obj) {
|
|
seq = pic_cons(pic, analyze(state, elt, false), seq);
|
|
}
|
|
return pic_reverse(pic, seq);
|
|
}
|
|
|
|
static pic_value
|
|
analyze_values(analyze_state *state, pic_value obj, bool tailpos)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value v, seq;
|
|
|
|
if (! tailpos) {
|
|
return analyze_call(state, obj, false);
|
|
}
|
|
|
|
seq = pic_list1(pic, pic_symbol_value(state->sRETURN));
|
|
pic_for_each (v, pic_cdr(pic, obj)) {
|
|
seq = pic_cons(pic, analyze(state, v, false), seq);
|
|
}
|
|
return pic_reverse(pic, seq);
|
|
}
|
|
|
|
static pic_value
|
|
analyze_call_with_values(analyze_state *state, pic_value obj, bool tailpos)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value prod, cnsm;
|
|
pic_sym call;
|
|
|
|
if (pic_length(pic, obj) != 3) {
|
|
pic_errorf(pic, "wrong number of arguments");
|
|
}
|
|
|
|
if (! tailpos) {
|
|
call = state->sCALL_WITH_VALUES;
|
|
} else {
|
|
call = state->sTAILCALL_WITH_VALUES;
|
|
}
|
|
prod = analyze(state, pic_list_ref(pic, obj, 1), false);
|
|
cnsm = analyze(state, pic_list_ref(pic, obj, 2), false);
|
|
return pic_list3(pic, pic_symbol_value(call), prod, cnsm);
|
|
}
|
|
|
|
#define ARGC_ASSERT(n) do { \
|
|
if (pic_length(pic, obj) != (n) + 1) { \
|
|
pic_errorf(pic, "wrong number of arguments"); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ARGC_ASSERT_WITH_FALLBACK(n) do { \
|
|
if (pic_length(pic, obj) != (n) + 1) { \
|
|
goto fallback; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define CONSTRUCT_OP1(op) \
|
|
pic_list2(pic, \
|
|
pic_symbol_value(op), \
|
|
analyze(state, pic_list_ref(pic, obj, 1), false))
|
|
|
|
#define CONSTRUCT_OP2(op) \
|
|
pic_list3(pic, \
|
|
pic_symbol_value(op), \
|
|
analyze(state, pic_list_ref(pic, obj, 1), false), \
|
|
analyze(state, pic_list_ref(pic, obj, 2), false))
|
|
|
|
static pic_value
|
|
analyze_node(analyze_state *state, pic_value obj, bool tailpos)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
|
|
switch (pic_type(obj)) {
|
|
case PIC_TT_SYMBOL: {
|
|
return analyze_var(state, pic_sym(obj));
|
|
}
|
|
case PIC_TT_PAIR: {
|
|
pic_value proc;
|
|
|
|
if (! pic_list_p(obj)) {
|
|
pic_errorf(pic, "invalid expression given: ~s", obj);
|
|
}
|
|
|
|
proc = pic_list_ref(pic, obj, 0);
|
|
if (pic_sym_p(proc)) {
|
|
pic_sym sym = pic_sym(proc);
|
|
|
|
if (sym == pic->rDEFINE) {
|
|
return analyze_define(state, obj);
|
|
}
|
|
else if (sym == pic->rLAMBDA) {
|
|
return analyze_lambda(state, obj);
|
|
}
|
|
else if (sym == pic->rIF) {
|
|
return analyze_if(state, obj, tailpos);
|
|
}
|
|
else if (sym == pic->rBEGIN) {
|
|
return analyze_begin(state, obj, tailpos);
|
|
}
|
|
else if (sym == pic->rSETBANG) {
|
|
return analyze_set(state, obj);
|
|
}
|
|
else if (sym == pic->rQUOTE) {
|
|
return analyze_quote(state, obj);
|
|
}
|
|
else if (sym == state->rCONS) {
|
|
ARGC_ASSERT(2);
|
|
return CONSTRUCT_OP2(pic->sCONS);
|
|
}
|
|
else if (sym == state->rCAR) {
|
|
ARGC_ASSERT(1);
|
|
return CONSTRUCT_OP1(pic->sCAR);
|
|
}
|
|
else if (sym == state->rCDR) {
|
|
ARGC_ASSERT(1);
|
|
return CONSTRUCT_OP1(pic->sCDR);
|
|
}
|
|
else if (sym == state->rNILP) {
|
|
ARGC_ASSERT(1);
|
|
return CONSTRUCT_OP1(pic->sNILP);
|
|
}
|
|
else if (sym == state->rADD) {
|
|
return analyze_add(state, obj, tailpos);
|
|
}
|
|
else if (sym == state->rSUB) {
|
|
return analyze_sub(state, obj);
|
|
}
|
|
else if (sym == state->rMUL) {
|
|
return analyze_mul(state, obj, tailpos);
|
|
}
|
|
else if (sym == state->rDIV) {
|
|
return analyze_div(state, obj);
|
|
}
|
|
else if (sym == state->rEQ) {
|
|
ARGC_ASSERT_WITH_FALLBACK(2);
|
|
return CONSTRUCT_OP2(pic->sEQ);
|
|
}
|
|
else if (sym == state->rLT) {
|
|
ARGC_ASSERT_WITH_FALLBACK(2);
|
|
return CONSTRUCT_OP2(pic->sLT);
|
|
}
|
|
else if (sym == state->rLE) {
|
|
ARGC_ASSERT_WITH_FALLBACK(2);
|
|
return CONSTRUCT_OP2(pic->sLE);
|
|
}
|
|
else if (sym == state->rGT) {
|
|
ARGC_ASSERT_WITH_FALLBACK(2);
|
|
return CONSTRUCT_OP2(pic->sGT);
|
|
}
|
|
else if (sym == state->rGE) {
|
|
ARGC_ASSERT_WITH_FALLBACK(2);
|
|
return CONSTRUCT_OP2(pic->sGE);
|
|
}
|
|
else if (sym == state->rNOT) {
|
|
ARGC_ASSERT(1);
|
|
return CONSTRUCT_OP1(pic->sNOT);
|
|
}
|
|
else if (sym == state->rVALUES) {
|
|
return analyze_values(state, obj, tailpos);
|
|
}
|
|
else if (sym == state->rCALL_WITH_VALUES) {
|
|
return analyze_call_with_values(state, obj, tailpos);
|
|
}
|
|
}
|
|
fallback:
|
|
|
|
return analyze_call(state, obj, tailpos);
|
|
}
|
|
default:
|
|
return pic_list2(pic, pic_symbol_value(pic->sQUOTE), obj);
|
|
}
|
|
}
|
|
|
|
pic_value
|
|
pic_analyze(pic_state *pic, pic_value obj)
|
|
{
|
|
analyze_state *state;
|
|
|
|
state = new_analyze_state(pic);
|
|
|
|
obj = analyze(state, obj, true);
|
|
|
|
analyze_deferred(state);
|
|
|
|
destroy_analyze_state(state);
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* scope object
|
|
*/
|
|
|
|
typedef struct codegen_context {
|
|
pic_sym name;
|
|
/* rest args variable is counted as a local */
|
|
bool varg;
|
|
xvect args, locals, captures;
|
|
/* actual bit code sequence */
|
|
pic_code *code;
|
|
size_t clen, ccapa;
|
|
/* child ireps */
|
|
struct pic_irep **irep;
|
|
size_t ilen, icapa;
|
|
/* constant object pool */
|
|
pic_value *pool;
|
|
size_t plen, pcapa;
|
|
|
|
struct codegen_context *up;
|
|
} codegen_context;
|
|
|
|
/**
|
|
* global codegen state
|
|
*/
|
|
|
|
typedef struct codegen_state {
|
|
pic_state *pic;
|
|
codegen_context *cxt;
|
|
pic_sym sGREF, sCREF, sLREF;
|
|
pic_sym sCALL, sTAILCALL, sRETURN;
|
|
pic_sym sCALL_WITH_VALUES, sTAILCALL_WITH_VALUES;
|
|
} codegen_state;
|
|
|
|
static void push_codegen_context(codegen_state *, pic_value, pic_value, pic_value, bool, pic_value);
|
|
static struct pic_irep *pop_codegen_context(codegen_state *);
|
|
|
|
static codegen_state *
|
|
new_codegen_state(pic_state *pic)
|
|
{
|
|
codegen_state *state;
|
|
|
|
state = pic_alloc(pic, sizeof(codegen_state));
|
|
state->pic = pic;
|
|
state->cxt = NULL;
|
|
|
|
register_symbol(pic, state, sCALL, "call");
|
|
register_symbol(pic, state, sTAILCALL, "tail-call");
|
|
register_symbol(pic, state, sGREF, "gref");
|
|
register_symbol(pic, state, sLREF, "lref");
|
|
register_symbol(pic, state, sCREF, "cref");
|
|
register_symbol(pic, state, sRETURN, "return");
|
|
register_symbol(pic, state, sCALL_WITH_VALUES, "call-with-values");
|
|
register_symbol(pic, state, sTAILCALL_WITH_VALUES, "tailcall-with-values");
|
|
|
|
push_codegen_context(state, pic_false_value(), pic_nil_value(), pic_nil_value(), false, pic_nil_value());
|
|
|
|
return state;
|
|
}
|
|
|
|
static struct pic_irep *
|
|
destroy_codegen_state(codegen_state *state)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
struct pic_irep *irep;
|
|
|
|
irep = pop_codegen_context(state);
|
|
pic_free(pic, state);
|
|
|
|
return irep;
|
|
}
|
|
|
|
static void
|
|
create_activation(codegen_context *cxt)
|
|
{
|
|
size_t i, n;
|
|
xhash regs;
|
|
pic_sym *var;
|
|
size_t offset;
|
|
|
|
xh_init_int(®s, sizeof(size_t));
|
|
|
|
offset = 1;
|
|
for (i = 0; i < xv_size(&cxt->args); ++i) {
|
|
var = xv_get(&cxt->args, i);
|
|
n = i + offset;
|
|
xh_put_int(®s, *var, &n);
|
|
}
|
|
offset += i;
|
|
for (i = 0; i < xv_size(&cxt->locals); ++i) {
|
|
var = xv_get(&cxt->locals, i);
|
|
n = i + offset;
|
|
xh_put_int(®s, *var, &n);
|
|
}
|
|
|
|
for (i = 0; i < xv_size(&cxt->captures); ++i) {
|
|
var = xv_get(&cxt->captures, i);
|
|
if ((n = xh_val(xh_get_int(®s, *var), size_t)) <= xv_size(&cxt->args) || (cxt->varg && n == xv_size(&cxt->args) + 1)) {
|
|
/* copy arguments to capture variable area */
|
|
cxt->code[cxt->clen].insn = OP_LREF;
|
|
cxt->code[cxt->clen].u.i = n;
|
|
cxt->clen++;
|
|
} else {
|
|
/* otherwise, just extend the stack */
|
|
cxt->code[cxt->clen].insn = OP_PUSHNONE;
|
|
cxt->clen++;
|
|
}
|
|
}
|
|
|
|
xh_destroy(®s);
|
|
}
|
|
|
|
static void
|
|
push_codegen_context(codegen_state *state, pic_value name, pic_value args, pic_value locals, bool varg, pic_value captures)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
codegen_context *cxt;
|
|
pic_value var;
|
|
pic_sym sym;
|
|
|
|
assert(pic_sym_p(name) || pic_false_p(name));
|
|
|
|
cxt = pic_alloc(pic, sizeof(codegen_context));
|
|
cxt->up = state->cxt;
|
|
cxt->name = pic_false_p(name)
|
|
? pic_intern_cstr(pic, "(anonymous lambda)")
|
|
: pic_sym(name);
|
|
cxt->varg = varg;
|
|
|
|
xv_init(&cxt->args, sizeof(pic_sym));
|
|
xv_init(&cxt->locals, sizeof(pic_sym));
|
|
xv_init(&cxt->captures, sizeof(pic_sym));
|
|
|
|
pic_for_each (var, args) {
|
|
sym = pic_sym(var);
|
|
xv_push(&cxt->args, &sym);
|
|
}
|
|
pic_for_each (var, locals) {
|
|
sym = pic_sym(var);
|
|
xv_push(&cxt->locals, &sym);
|
|
}
|
|
pic_for_each (var, captures) {
|
|
sym = pic_sym(var);
|
|
xv_push(&cxt->captures, &sym);
|
|
}
|
|
|
|
cxt->code = pic_calloc(pic, PIC_ISEQ_SIZE, sizeof(pic_code));
|
|
cxt->clen = 0;
|
|
cxt->ccapa = PIC_ISEQ_SIZE;
|
|
|
|
cxt->irep = pic_calloc(pic, PIC_IREP_SIZE, sizeof(struct pic_irep *));
|
|
cxt->ilen = 0;
|
|
cxt->icapa = PIC_IREP_SIZE;
|
|
|
|
cxt->pool = pic_calloc(pic, PIC_POOL_SIZE, sizeof(pic_value));
|
|
cxt->plen = 0;
|
|
cxt->pcapa = PIC_POOL_SIZE;
|
|
|
|
state->cxt = cxt;
|
|
|
|
create_activation(cxt);
|
|
}
|
|
|
|
static struct pic_irep *
|
|
pop_codegen_context(codegen_state *state)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
codegen_context *cxt = state->cxt;
|
|
struct pic_irep *irep;
|
|
|
|
/* create irep */
|
|
irep = (struct pic_irep *)pic_obj_alloc(pic, sizeof(struct pic_irep), PIC_TT_IREP);
|
|
irep->name = state->cxt->name;
|
|
irep->varg = state->cxt->varg;
|
|
irep->argc = xv_size(&state->cxt->args) + 1;
|
|
irep->localc = xv_size(&state->cxt->locals);
|
|
irep->capturec = xv_size(&state->cxt->captures);
|
|
irep->code = pic_realloc(pic, state->cxt->code, sizeof(pic_code) * state->cxt->clen);
|
|
irep->clen = state->cxt->clen;
|
|
irep->irep = pic_realloc(pic, state->cxt->irep, sizeof(struct pic_irep *) * state->cxt->ilen);
|
|
irep->ilen = state->cxt->ilen;
|
|
irep->pool = pic_realloc(pic, state->cxt->pool, sizeof(pic_value) * state->cxt->plen);
|
|
irep->plen = state->cxt->plen;
|
|
|
|
/* finalize */
|
|
xv_destroy(&cxt->args);
|
|
xv_destroy(&cxt->locals);
|
|
xv_destroy(&cxt->captures);
|
|
|
|
/* destroy context */
|
|
cxt = cxt->up;
|
|
pic_free(pic, state->cxt);
|
|
state->cxt = cxt;
|
|
|
|
return irep;
|
|
}
|
|
|
|
static int
|
|
index_capture(codegen_state *state, pic_sym sym, int depth)
|
|
{
|
|
codegen_context *cxt = state->cxt;
|
|
size_t i;
|
|
pic_sym *var;
|
|
|
|
while (depth-- > 0) {
|
|
cxt = cxt->up;
|
|
}
|
|
|
|
for (i = 0; i < xv_size(&cxt->captures); ++i) {
|
|
var = xv_get(&cxt->captures, i);
|
|
if (*var == sym)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
index_local(codegen_state *state, pic_sym sym)
|
|
{
|
|
codegen_context *cxt = state->cxt;
|
|
size_t i, offset;
|
|
pic_sym *var;
|
|
|
|
offset = 1;
|
|
for (i = 0; i < xv_size(&cxt->args); ++i) {
|
|
var = xv_get(&cxt->args, i);
|
|
if (*var == sym)
|
|
return i + offset;
|
|
}
|
|
offset += i;
|
|
for (i = 0; i < xv_size(&cxt->locals); ++i) {
|
|
var = xv_get(&cxt->locals, i);
|
|
if (*var == sym)
|
|
return i + offset;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static struct pic_irep *codegen_lambda(codegen_state *, pic_value);
|
|
|
|
static void
|
|
codegen(codegen_state *state, pic_value obj)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
codegen_context *cxt = state->cxt;
|
|
pic_sym sym;
|
|
|
|
sym = pic_sym(pic_car(pic, obj));
|
|
if (sym == state->sGREF) {
|
|
cxt->code[cxt->clen].insn = OP_GREF;
|
|
cxt->code[cxt->clen].u.i = pic_sym(pic_list_ref(pic, obj, 1));
|
|
cxt->clen++;
|
|
return;
|
|
} else if (sym == state->sCREF) {
|
|
pic_sym name;
|
|
int depth;
|
|
|
|
depth = pic_int(pic_list_ref(pic, obj, 1));
|
|
name = pic_sym(pic_list_ref(pic, obj, 2));
|
|
cxt->code[cxt->clen].insn = OP_CREF;
|
|
cxt->code[cxt->clen].u.r.depth = depth;
|
|
cxt->code[cxt->clen].u.r.idx = index_capture(state, name, depth);
|
|
cxt->clen++;
|
|
return;
|
|
} else if (sym == state->sLREF) {
|
|
pic_sym name;
|
|
int i;
|
|
|
|
name = pic_sym(pic_list_ref(pic, obj, 1));
|
|
if ((i = index_capture(state, name, 0)) != -1) {
|
|
cxt->code[cxt->clen].insn = OP_LREF;
|
|
cxt->code[cxt->clen].u.i = i + xv_size(&cxt->args) + xv_size(&cxt->locals) + 1;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
cxt->code[cxt->clen].insn = OP_LREF;
|
|
cxt->code[cxt->clen].u.i = index_local(state, name);
|
|
cxt->clen++;
|
|
return;
|
|
} else if (sym == pic->sSETBANG) {
|
|
pic_value var, val;
|
|
pic_sym type;
|
|
|
|
val = pic_list_ref(pic, obj, 2);
|
|
codegen(state, val);
|
|
|
|
var = pic_list_ref(pic, obj, 1);
|
|
type = pic_sym(pic_list_ref(pic, var, 0));
|
|
if (type == state->sGREF) {
|
|
cxt->code[cxt->clen].insn = OP_GSET;
|
|
cxt->code[cxt->clen].u.i = pic_int(pic_list_ref(pic, var, 1));
|
|
cxt->clen++;
|
|
cxt->code[cxt->clen].insn = OP_PUSHNONE;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (type == state->sCREF) {
|
|
pic_sym name;
|
|
int depth;
|
|
|
|
depth = pic_int(pic_list_ref(pic, var, 1));
|
|
name = pic_sym(pic_list_ref(pic, var, 2));
|
|
cxt->code[cxt->clen].insn = OP_CSET;
|
|
cxt->code[cxt->clen].u.r.depth = depth;
|
|
cxt->code[cxt->clen].u.r.idx = index_capture(state, name, depth);
|
|
cxt->clen++;
|
|
cxt->code[cxt->clen].insn = OP_PUSHNONE;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (type == state->sLREF) {
|
|
pic_sym name;
|
|
int i;
|
|
|
|
name = pic_sym(pic_list_ref(pic, var, 1));
|
|
if ((i = index_capture(state, name, 0)) != -1) {
|
|
cxt->code[cxt->clen].insn = OP_LSET;
|
|
cxt->code[cxt->clen].u.i = i + xv_size(&cxt->args) + xv_size(&cxt->locals) + 1;
|
|
cxt->clen++;
|
|
cxt->code[cxt->clen].insn = OP_PUSHNONE;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
cxt->code[cxt->clen].insn = OP_LSET;
|
|
cxt->code[cxt->clen].u.i = index_local(state, name);
|
|
cxt->clen++;
|
|
cxt->code[cxt->clen].insn = OP_PUSHNONE;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
}
|
|
else if (sym == pic->sLAMBDA) {
|
|
int k;
|
|
|
|
if (cxt->ilen >= cxt->icapa) {
|
|
cxt->icapa *= 2;
|
|
cxt->irep = pic_realloc(pic, cxt->irep, sizeof(struct pic_irep *) * cxt->icapa);
|
|
}
|
|
k = cxt->ilen++;
|
|
cxt->code[cxt->clen].insn = OP_LAMBDA;
|
|
cxt->code[cxt->clen].u.i = k;
|
|
cxt->clen++;
|
|
|
|
cxt->irep[k] = codegen_lambda(state, obj);
|
|
return;
|
|
}
|
|
else if (sym == pic->sIF) {
|
|
int s, t;
|
|
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
|
|
cxt->code[cxt->clen].insn = OP_JMPIF;
|
|
s = cxt->clen++;
|
|
|
|
/* if false branch */
|
|
codegen(state, pic_list_ref(pic, obj, 3));
|
|
cxt->code[cxt->clen].insn = OP_JMP;
|
|
t = cxt->clen++;
|
|
|
|
cxt->code[s].u.i = cxt->clen - s;
|
|
|
|
/* if true branch */
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
cxt->code[t].u.i = cxt->clen - t;
|
|
return;
|
|
}
|
|
else if (sym == pic->sBEGIN) {
|
|
pic_value elt;
|
|
int i = 0;
|
|
|
|
pic_for_each (elt, pic_cdr(pic, obj)) {
|
|
if (i++ != 0) {
|
|
cxt->code[cxt->clen].insn = OP_POP;
|
|
cxt->clen++;
|
|
}
|
|
codegen(state, elt);
|
|
}
|
|
return;
|
|
}
|
|
else if (sym == pic->sQUOTE) {
|
|
int pidx;
|
|
|
|
obj = pic_list_ref(pic, obj, 1);
|
|
switch (pic_type(obj)) {
|
|
case PIC_TT_BOOL:
|
|
if (pic_true_p(obj)) {
|
|
cxt->code[cxt->clen].insn = OP_PUSHTRUE;
|
|
} else {
|
|
cxt->code[cxt->clen].insn = OP_PUSHFALSE;
|
|
}
|
|
cxt->clen++;
|
|
return;
|
|
case PIC_TT_INT:
|
|
cxt->code[cxt->clen].insn = OP_PUSHINT;
|
|
cxt->code[cxt->clen].u.i = pic_int(obj);
|
|
cxt->clen++;
|
|
return;
|
|
case PIC_TT_NIL:
|
|
cxt->code[cxt->clen].insn = OP_PUSHNIL;
|
|
cxt->clen++;
|
|
return;
|
|
case PIC_TT_CHAR:
|
|
cxt->code[cxt->clen].insn = OP_PUSHCHAR;
|
|
cxt->code[cxt->clen].u.c = pic_char(obj);
|
|
cxt->clen++;
|
|
return;
|
|
default:
|
|
if (cxt->plen >= cxt->pcapa) {
|
|
cxt->pcapa *= 2;
|
|
cxt->pool = pic_realloc(pic, cxt->pool, sizeof(pic_value) * cxt->pcapa);
|
|
}
|
|
pidx = cxt->plen++;
|
|
cxt->pool[pidx] = obj;
|
|
cxt->code[cxt->clen].insn = OP_PUSHCONST;
|
|
cxt->code[cxt->clen].u.i = pidx;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
}
|
|
else if (sym == pic->sCONS) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
cxt->code[cxt->clen].insn = OP_CONS;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sCAR) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
cxt->code[cxt->clen].insn = OP_CAR;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sCDR) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
cxt->code[cxt->clen].insn = OP_CDR;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sNILP) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
cxt->code[cxt->clen].insn = OP_NILP;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sADD) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
cxt->code[cxt->clen].insn = OP_ADD;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sSUB) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
cxt->code[cxt->clen].insn = OP_SUB;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sMUL) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
cxt->code[cxt->clen].insn = OP_MUL;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sDIV) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
cxt->code[cxt->clen].insn = OP_DIV;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sMINUS) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
cxt->code[cxt->clen].insn = OP_MINUS;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sEQ) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
cxt->code[cxt->clen].insn = OP_EQ;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sLT) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
cxt->code[cxt->clen].insn = OP_LT;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sLE) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
cxt->code[cxt->clen].insn = OP_LE;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sGT) {
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
cxt->code[cxt->clen].insn = OP_LT;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sGE) {
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
cxt->code[cxt->clen].insn = OP_LE;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == pic->sNOT) {
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
cxt->code[cxt->clen].insn = OP_NOT;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == state->sCALL || sym == state->sTAILCALL) {
|
|
int len = pic_length(pic, obj);
|
|
pic_value elt;
|
|
|
|
pic_for_each (elt, pic_cdr(pic, obj)) {
|
|
codegen(state, elt);
|
|
}
|
|
cxt->code[cxt->clen].insn = (sym == state->sCALL) ? OP_CALL : OP_TAILCALL;
|
|
cxt->code[cxt->clen].u.i = len - 1;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == state->sCALL_WITH_VALUES || sym == state->sTAILCALL_WITH_VALUES) {
|
|
/* stack consumer at first */
|
|
codegen(state, pic_list_ref(pic, obj, 2));
|
|
codegen(state, pic_list_ref(pic, obj, 1));
|
|
/* call producer */
|
|
cxt->code[cxt->clen].insn = OP_CALL;
|
|
cxt->code[cxt->clen].u.i = 1;
|
|
cxt->clen++;
|
|
/* call consumer */
|
|
cxt->code[cxt->clen].insn = (sym == state->sCALL_WITH_VALUES) ? OP_CALL : OP_TAILCALL;
|
|
cxt->code[cxt->clen].u.i = -1;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
else if (sym == state->sRETURN) {
|
|
int len = pic_length(pic, obj);
|
|
pic_value elt;
|
|
|
|
pic_for_each (elt, pic_cdr(pic, obj)) {
|
|
codegen(state, elt);
|
|
}
|
|
cxt->code[cxt->clen].insn = OP_RET;
|
|
cxt->code[cxt->clen].u.i = len - 1;
|
|
cxt->clen++;
|
|
return;
|
|
}
|
|
pic_errorf(pic, "codegen: unknown AST type");
|
|
}
|
|
|
|
static struct pic_irep *
|
|
codegen_lambda(codegen_state *state, pic_value obj)
|
|
{
|
|
pic_state *pic = state->pic;
|
|
pic_value name, args, locals, closes, body;
|
|
bool varg;
|
|
|
|
name = pic_list_ref(pic, obj, 1);
|
|
args = pic_list_ref(pic, obj, 2);
|
|
locals = pic_list_ref(pic, obj, 3);
|
|
varg = pic_true_p(pic_list_ref(pic, obj, 4));
|
|
closes = pic_list_ref(pic, obj, 5);
|
|
body = pic_list_ref(pic, obj, 6);
|
|
|
|
/* inner environment */
|
|
push_codegen_context(state, name, args, locals, varg, closes);
|
|
{
|
|
/* body */
|
|
codegen(state, body);
|
|
}
|
|
return pop_codegen_context(state);
|
|
}
|
|
|
|
struct pic_irep *
|
|
pic_codegen(pic_state *pic, pic_value obj)
|
|
{
|
|
codegen_state *state;
|
|
|
|
state = new_codegen_state(pic);
|
|
|
|
codegen(state, obj);
|
|
|
|
return destroy_codegen_state(state);
|
|
}
|
|
|
|
struct pic_proc *
|
|
pic_compile(pic_state *pic, pic_value obj, struct pic_lib *lib)
|
|
{
|
|
struct pic_irep *irep;
|
|
size_t ai = pic_gc_arena_preserve(pic);
|
|
|
|
#if DEBUG
|
|
fprintf(stdout, "ai = %zu\n", pic_gc_arena_preserve(pic));
|
|
|
|
fprintf(stdout, "# input expression\n");
|
|
pic_debug(pic, obj);
|
|
fprintf(stdout, "\n");
|
|
|
|
fprintf(stdout, "ai = %zu\n", pic_gc_arena_preserve(pic));
|
|
#endif
|
|
|
|
/* macroexpand */
|
|
obj = pic_macroexpand(pic, obj, lib);
|
|
#if DEBUG
|
|
fprintf(stdout, "## macroexpand completed\n");
|
|
pic_debug(pic, obj);
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout, "ai = %zu\n", pic_gc_arena_preserve(pic));
|
|
#endif
|
|
|
|
/* analyze */
|
|
obj = pic_analyze(pic, obj);
|
|
#if DEBUG
|
|
fprintf(stdout, "## analyzer completed\n");
|
|
pic_debug(pic, obj);
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout, "ai = %zu\n", pic_gc_arena_preserve(pic));
|
|
#endif
|
|
|
|
/* codegen */
|
|
irep = pic_codegen(pic, obj);
|
|
#if DEBUG
|
|
fprintf(stdout, "## codegen completed\n");
|
|
pic_dump_irep(irep);
|
|
#endif
|
|
|
|
#if DEBUG
|
|
fprintf(stdout, "# compilation finished\n");
|
|
puts("");
|
|
#endif
|
|
|
|
pic_gc_arena_restore(pic, ai);
|
|
pic_gc_protect(pic, pic_obj_value(irep));
|
|
|
|
return pic_make_proc_irep(pic, irep, NULL);
|
|
}
|