picrin/src/vm.c

1031 lines
22 KiB
C
Raw Normal View History

2014-01-17 06:58:31 -05:00
/**
* See Copyright Notice in picrin.h
*/
#include <stdlib.h>
#include <stdarg.h>
2013-10-28 08:26:39 -04:00
#include <limits.h>
#include <math.h>
2013-10-11 11:16:19 -04:00
#include "picrin.h"
2013-10-19 23:34:57 -04:00
#include "picrin/pair.h"
#include "picrin/string.h"
#include "picrin/vector.h"
2013-10-20 04:06:47 -04:00
#include "picrin/proc.h"
2014-01-12 10:48:00 -05:00
#include "picrin/port.h"
2013-10-20 04:06:47 -04:00
#include "picrin/irep.h"
2013-11-04 22:58:16 -05:00
#include "picrin/blob.h"
2014-01-12 02:09:27 -05:00
#include "picrin/var.h"
#include "picrin/lib.h"
#include "picrin/macro.h"
2014-03-24 01:09:28 -04:00
#include "picrin/error.h"
#include "picrin/dict.h"
#define GET_OPERAND(pic,n) ((pic)->ci->fp[(n)])
2013-11-09 00:12:59 -05:00
struct pic_proc *
pic_get_proc(pic_state *pic)
{
pic_value v = GET_OPERAND(pic,0);
if (! pic_proc_p(v)) {
pic_error(pic, "fatal error");
}
return pic_proc_ptr(v);
}
2014-06-14 03:08:12 -04:00
/**
* char type
* ---- ----
* o object
* i int
* I int with exactness
* f float
* F float with exactness
* s string object
* z c string
* m symbol
* v vector object
* b bytevector object
* c char
* l lambda object
* p port object
* d dictionary object
2014-06-14 03:08:12 -04:00
*
* | optional operator
* * variable length operator
*/
int
pic_get_args(pic_state *pic, const char *format, ...)
{
char c;
int i = 1, argc = pic->ci->argc;
va_list ap;
bool opt = false;
va_start(ap, format);
while ((c = *format++)) {
switch (c) {
default:
if (argc <= i && ! opt) {
pic_error(pic, "wrong number of arguments");
}
break;
case '|':
break;
2013-11-15 08:53:41 -05:00
case '*':
break;
}
/* in order to run out of all arguments passed to this function
(i.e. do va_arg for each argument), optional argument existence
check is done in every case closure */
2013-11-15 08:53:41 -05:00
if (c == '*')
break;
switch (c) {
case '|':
opt = true;
break;
2014-01-16 23:16:55 -05:00
case 'o': {
pic_value *p;
p = va_arg(ap, pic_value*);
if (i < argc) {
*p = GET_OPERAND(pic,i);
i++;
}
break;
2014-01-16 23:16:55 -05:00
}
case 'f': {
double *f;
f = va_arg(ap, double *);
if (i < argc) {
pic_value v;
v = GET_OPERAND(pic, i);
switch (pic_type(v)) {
case PIC_TT_FLOAT:
*f = pic_float(v);
break;
case PIC_TT_INT:
*f = pic_int(v);
break;
default:
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args: expected float or int, but got ~s", v);
2014-01-16 23:16:55 -05:00
}
i++;
2013-10-20 22:42:21 -04:00
}
break;
2014-01-16 23:16:55 -05:00
}
case 'F': {
double *f;
bool *e;
f = va_arg(ap, double *);
e = va_arg(ap, bool *);
if (i < argc) {
pic_value v;
v = GET_OPERAND(pic, i);
switch (pic_type(v)) {
case PIC_TT_FLOAT:
*f = pic_float(v);
*e = false;
break;
case PIC_TT_INT:
*f = pic_int(v);
*e = true;
break;
default:
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args: expected float or int, but got ~s", v);
2014-01-16 23:16:55 -05:00
}
i++;
2013-10-27 11:21:24 -04:00
}
break;
2014-01-16 23:16:55 -05:00
}
case 'I': {
int *k;
bool *e;
k = va_arg(ap, int *);
e = va_arg(ap, bool *);
if (i < argc) {
pic_value v;
v = GET_OPERAND(pic, i);
switch (pic_type(v)) {
case PIC_TT_FLOAT:
*k = (int)pic_float(v);
*e = false;
break;
case PIC_TT_INT:
*k = pic_int(v);
*e = true;
break;
default:
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args: expected float or int, but got ~s", v);
2014-01-16 23:16:55 -05:00
}
i++;
}
break;
2014-01-16 23:16:55 -05:00
}
case 'i': {
int *k;
k = va_arg(ap, int *);
if (i < argc) {
pic_value v;
v = GET_OPERAND(pic, i);
switch (pic_type(v)) {
case PIC_TT_FLOAT:
*k = (int)pic_float(v);
break;
case PIC_TT_INT:
*k = pic_int(v);
break;
default:
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args: expected int, but got ~s", v);
2014-01-16 23:16:55 -05:00
}
i++;
2013-11-04 20:53:33 -05:00
}
break;
2014-01-16 23:16:55 -05:00
}
case 's': {
pic_str **str;
pic_value v;
str = va_arg(ap, pic_str **);
if (i < argc) {
v = GET_OPERAND(pic,i);
if (pic_str_p(v)) {
*str = pic_str_ptr(v);
}
else {
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args: expected string, but got ~s", v);
}
i++;
}
break;
}
case 'z': {
const char **cstr;
2014-07-16 22:30:44 -04:00
pic_value v;
2014-01-16 23:16:55 -05:00
cstr = va_arg(ap, const char **);
2014-01-16 23:16:55 -05:00
if (i < argc) {
2014-07-16 22:30:44 -04:00
v = GET_OPERAND(pic,i);
if (! pic_str_p(v)) {
pic_errorf(pic, "pic_get_args: expected string, but got ~s", v);
2014-01-16 23:16:55 -05:00
}
2014-07-16 22:30:44 -04:00
*cstr = pic_str_cstr(pic_str_ptr(v));
2014-01-16 23:16:55 -05:00
i++;
2013-10-15 10:25:07 -04:00
}
break;
2014-01-16 23:16:55 -05:00
}
case 'm': {
pic_sym *m;
pic_value v;
m = va_arg(ap, pic_sym *);
if (i < argc) {
v = GET_OPERAND(pic,i);
2014-01-30 13:03:36 -05:00
if (pic_sym_p(v)) {
2014-01-16 23:16:55 -05:00
*m = pic_sym(v);
}
else {
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args: expected symbol, but got ~s", v);
2014-01-16 23:16:55 -05:00
}
i++;
2014-01-09 02:33:38 -05:00
}
2014-01-16 23:16:55 -05:00
break;
}
case 'v': {
struct pic_vector **vec;
pic_value v;
vec = va_arg(ap, struct pic_vector **);
if (i < argc) {
v = GET_OPERAND(pic,i);
if (pic_vec_p(v)) {
*vec = pic_vec_ptr(v);
}
else {
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args: expected vector, but got ~s", v);
2014-01-16 23:16:55 -05:00
}
i++;
2013-11-04 20:53:33 -05:00
}
break;
2014-01-16 23:16:55 -05:00
}
case 'b': {
struct pic_blob **b;
pic_value v;
b = va_arg(ap, struct pic_blob **);
if (i < argc) {
v = GET_OPERAND(pic,i);
if (pic_blob_p(v)) {
*b = pic_blob_ptr(v);
}
else {
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args: expected bytevector, but got ~s", v);
2014-01-16 23:16:55 -05:00
}
i++;
2013-11-04 22:58:16 -05:00
}
break;
2014-01-16 23:16:55 -05:00
}
case 'c': {
char *c;
pic_value v;
c = va_arg(ap, char *);
if (i < argc) {
v = GET_OPERAND(pic,i);
if (pic_char_p(v)) {
*c = pic_char(v);
}
else {
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args: expected char, but got ~s", v);
2014-01-16 23:16:55 -05:00
}
i++;
2013-11-14 06:41:22 -05:00
}
break;
2014-01-16 23:16:55 -05:00
}
case 'l': {
struct pic_proc **l;
pic_value v;
l = va_arg(ap, struct pic_proc **);
if (i < argc) {
v = GET_OPERAND(pic,i);
if (pic_proc_p(v)) {
*l = pic_proc_ptr(v);
2014-01-08 06:53:28 -05:00
}
2014-01-16 23:16:55 -05:00
else {
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args, expected procedure, but got ~s", v);
2014-01-16 23:16:55 -05:00
}
i++;
2014-01-08 06:53:28 -05:00
}
2014-01-16 23:16:55 -05:00
break;
}
case 'p': {
struct pic_port **p;
pic_value v;
p = va_arg(ap, struct pic_port **);
if (i < argc) {
v = GET_OPERAND(pic,i);
if (pic_port_p(v)) {
*p = pic_port_ptr(v);
}
else {
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args, expected port, but got ~s", v);
2014-01-12 10:48:00 -05:00
}
2014-01-16 23:16:55 -05:00
i++;
2014-01-12 10:48:00 -05:00
}
2014-01-16 23:16:55 -05:00
break;
}
case 'd': {
struct pic_dict **d;
pic_value v;
d = va_arg(ap, struct pic_dict **);
if (i < argc) {
v = GET_OPERAND(pic,i);
if (pic_dict_p(v)) {
*d = pic_dict_ptr(v);
}
else {
2014-07-16 22:30:44 -04:00
pic_errorf(pic, "pic_get_args, expected dictionary, but got ~s", v);
}
i++;
}
break;
}
default:
2014-01-16 23:16:55 -05:00
pic_error(pic, "pic_get_args: invalid argument specifier given");
}
}
2013-11-15 08:53:41 -05:00
if ('*' == c) {
2013-11-17 11:07:51 -05:00
size_t *n;
2013-11-15 08:53:41 -05:00
pic_value **argv;
2013-11-17 11:07:51 -05:00
n = va_arg(ap, size_t *);
2013-11-15 08:53:41 -05:00
argv = va_arg(ap, pic_value **);
if (i <= argc) {
*n = argc - i;
*argv = &GET_OPERAND(pic, i);
i = argc;
}
}
else if (argc > i) {
pic_error(pic, "wrong number of arguments");
}
va_end(ap);
2014-02-01 00:23:21 -05:00
return i - 1;
}
static size_t
global_ref(pic_state *pic, const char *name)
{
xh_entry *e;
2014-03-25 01:12:53 -04:00
pic_sym sym, rename;
sym = pic_intern_cstr(pic, name);
2014-07-20 02:30:48 -04:00
if (! pic_find_rename(pic, pic->lib->env, sym, &rename)) {
return SIZE_MAX;
}
2014-06-14 07:59:31 -04:00
if (! (e = xh_get_int(&pic->global_tbl, rename))) {
return SIZE_MAX;
}
2014-03-25 02:29:26 -04:00
return xh_val(e, size_t);
}
static size_t
global_def(pic_state *pic, const char *name)
{
2014-03-25 01:12:53 -04:00
pic_sym sym, rename;
size_t gidx;
sym = pic_intern_cstr(pic, name);
if ((gidx = global_ref(pic, name)) != SIZE_MAX) {
pic_warn(pic, "redefining global");
return gidx;
}
/* register to the senv */
2014-07-20 02:30:48 -04:00
rename = pic_add_rename(pic, pic->lib->env, sym);
/* register to the global table */
gidx = pic->glen++;
if (pic->glen >= pic->gcapa) {
pic_error(pic, "global table overflow");
}
2014-06-14 07:59:31 -04:00
xh_put_int(&pic->global_tbl, rename, &gidx);
return gidx;
}
void
pic_define(pic_state *pic, const char *name, pic_value val)
{
/* push to the global arena */
pic->globals[global_def(pic, name)] = val;
/* export! */
pic_export(pic, pic_intern_cstr(pic, name));
}
pic_value
pic_ref(pic_state *pic, const char *name)
{
size_t gid;
gid = global_ref(pic, name);
if (gid == SIZE_MAX) {
2014-07-12 09:21:19 -04:00
pic_errorf(pic, "symbol \"%s\" not defined", name);
}
return pic->globals[gid];
}
void
pic_set(pic_state *pic, const char *name, pic_value value)
{
size_t gid;
gid = global_ref(pic, name);
if (gid == SIZE_MAX) {
pic_error(pic, "symbol not defined");
}
pic->globals[gid] = value;
}
2014-07-12 09:07:06 -04:00
pic_value
pic_funcall(pic_state *pic, const char *name, pic_list args)
{
pic_value proc;
proc = pic_ref(pic, name);
pic_assert_type(pic, proc, proc);
return pic_apply(pic, pic_proc_ptr(proc), args);
}
void
pic_defun(pic_state *pic, const char *name, pic_func_t cfunc)
{
struct pic_proc *proc;
2014-03-26 08:20:06 -04:00
proc = pic_proc_new(pic, cfunc, name);
pic_define(pic, name, pic_obj_value(proc));
}
2014-03-23 00:09:35 -04:00
static void
vm_push_env(pic_state *pic)
{
pic_callinfo *ci = pic->ci;
ci->env = (struct pic_env *)pic_obj_alloc(pic, offsetof(struct pic_env, storage) + sizeof(pic_value) * ci->regc, PIC_TT_ENV);
ci->env->up = ci->up;
ci->env->regc = ci->regc;
ci->env->regs = ci->regs;
}
static void
vm_tear_off(pic_state *pic)
{
struct pic_env *env;
int i;
2014-03-23 00:09:35 -04:00
assert(pic->ci->env != NULL);
env = pic->ci->env;
2014-03-22 23:45:36 -04:00
for (i = 0; i < env->regc; ++i) {
env->storage[i] = env->regs[i];
}
2014-03-22 23:45:36 -04:00
env->regs = env->storage;
}
2014-03-25 07:54:57 -04:00
pic_value
pic_apply0(pic_state *pic, struct pic_proc *proc)
{
return pic_apply(pic, proc, pic_nil_value());
}
pic_value
pic_apply1(pic_state *pic, struct pic_proc *proc, pic_value arg1)
{
return pic_apply(pic, proc, pic_list1(pic, arg1));
}
pic_value
pic_apply2(pic_state *pic, struct pic_proc *proc, pic_value arg1, pic_value arg2)
{
return pic_apply(pic, proc, pic_list2(pic, arg1, arg2));
}
pic_value
pic_apply3(pic_state *pic, struct pic_proc *proc, pic_value arg1, pic_value arg2, pic_value arg3)
{
return pic_apply(pic, proc, pic_list3(pic, arg1, arg2, arg3));
}
pic_value
pic_apply4(pic_state *pic, struct pic_proc *proc, pic_value arg1, pic_value arg2, pic_value arg3, pic_value arg4)
{
return pic_apply(pic, proc, pic_list4(pic, arg1, arg2, arg3, arg4));
}
pic_value
pic_apply5(pic_state *pic, struct pic_proc *proc, pic_value arg1, pic_value arg2, pic_value arg3, pic_value arg4, pic_value arg5)
{
return pic_apply(pic, proc, pic_list5(pic, arg1, arg2, arg3, arg4, arg5));
}
2013-11-01 06:02:46 -04:00
#if VM_DEBUG
2014-03-19 06:44:45 -04:00
# define OPCODE_EXEC_HOOK pic_dump_code(c)
2013-11-01 06:02:46 -04:00
#else
# define OPCODE_EXEC_HOOK ((void)0)
#endif
2013-10-17 00:54:48 -04:00
#if PIC_DIRECT_THREADED_VM
# define VM_LOOP JUMP;
2013-11-01 06:02:46 -04:00
# define CASE(x) L_##x: OPCODE_EXEC_HOOK;
2014-02-03 20:40:03 -05:00
# define NEXT pic->ip++; JUMP;
# define JUMP c = *pic->ip; goto *oplabels[c.insn];
2013-10-17 00:54:48 -04:00
# define VM_LOOP_END
#else
2014-02-03 20:40:03 -05:00
# define VM_LOOP for (;;) { c = *pic->ip; switch (c.insn) {
2013-10-17 00:54:48 -04:00
# define CASE(x) case x:
2014-02-03 20:40:03 -05:00
# define NEXT pic->ip++; break
2013-10-17 00:54:48 -04:00
# define JUMP break
# define VM_LOOP_END } }
#endif
2013-10-12 05:48:35 -04:00
#define PUSH(v) ((pic->sp >= pic->stend) ? abort() : (*pic->sp++ = (v)))
#define POP() (*--pic->sp)
#define PUSHCI() (++pic->ci)
#define POPCI() (pic->ci--)
2013-10-15 10:29:34 -04:00
2013-10-12 00:06:02 -04:00
pic_value
pic_apply(pic_state *pic, struct pic_proc *proc, pic_value argv)
2013-10-12 00:06:02 -04:00
{
2014-03-07 08:06:43 -05:00
pic_code c;
2014-05-26 03:06:41 -04:00
size_t ai = pic_gc_arena_preserve(pic);
size_t argc, i;
2014-03-07 08:06:43 -05:00
pic_code boot[2];
2013-10-12 00:06:02 -04:00
2013-10-17 00:54:48 -04:00
#if PIC_DIRECT_THREADED_VM
static void *oplabels[] = {
2014-02-04 02:20:17 -05:00
&&L_OP_NOP, &&L_OP_POP, &&L_OP_PUSHNIL, &&L_OP_PUSHTRUE, &&L_OP_PUSHFALSE,
2013-11-04 21:37:18 -05:00
&&L_OP_PUSHINT, &&L_OP_PUSHCHAR, &&L_OP_PUSHCONST,
&&L_OP_GREF, &&L_OP_GSET, &&L_OP_LREF, &&L_OP_LSET, &&L_OP_CREF, &&L_OP_CSET,
2014-02-02 00:54:47 -05:00
&&L_OP_JMP, &&L_OP_JMPIF, &&L_OP_NOT, &&L_OP_CALL, &&L_OP_TAILCALL, &&L_OP_RET,
&&L_OP_LAMBDA, &&L_OP_CONS, &&L_OP_CAR, &&L_OP_CDR, &&L_OP_NILP,
2013-11-06 22:17:37 -05:00
&&L_OP_ADD, &&L_OP_SUB, &&L_OP_MUL, &&L_OP_DIV, &&L_OP_MINUS,
2013-10-24 08:10:13 -04:00
&&L_OP_EQ, &&L_OP_LT, &&L_OP_LE, &&L_OP_STOP
2013-10-17 00:54:48 -04:00
};
#endif
if (! pic_list_p(argv)) {
pic_error(pic, "argv must be a proper list");
}
argc = pic_length(pic, argv) + 1;
2013-11-01 06:02:46 -04:00
#if VM_DEBUG
puts("### booting VM... ###");
pic_value *stbase = pic->sp;
pic_callinfo *cibase = pic->ci;
2013-11-01 06:02:46 -04:00
#endif
PUSH(pic_obj_value(proc));
for (i = 1; i < argc; ++i) {
PUSH(pic_car(pic, argv));
argv = pic_cdr(pic, argv);
}
/* boot! */
boot[0].insn = OP_CALL;
boot[0].u.i = argc;
boot[1].insn = OP_STOP;
2014-02-03 20:40:03 -05:00
pic->ip = boot;
2013-10-12 00:06:02 -04:00
2013-10-12 05:48:35 -04:00
VM_LOOP {
2014-02-04 02:20:17 -05:00
CASE(OP_NOP) {
NEXT;
}
CASE(OP_POP) {
2014-03-19 06:49:09 -04:00
POP();
NEXT;
}
2013-10-12 05:48:35 -04:00
CASE(OP_PUSHNIL) {
PUSH(pic_nil_value());
2013-10-16 00:17:01 -04:00
NEXT;
}
CASE(OP_PUSHTRUE) {
PUSH(pic_true_value());
NEXT;
}
CASE(OP_PUSHFALSE) {
PUSH(pic_false_value());
2013-10-12 05:48:35 -04:00
NEXT;
2013-10-12 00:06:02 -04:00
}
2013-10-27 11:21:24 -04:00
CASE(OP_PUSHINT) {
PUSH(pic_int_value(c.u.i));
2013-10-27 11:21:24 -04:00
NEXT;
}
2013-11-04 21:37:18 -05:00
CASE(OP_PUSHCHAR) {
PUSH(pic_char_value(c.u.c));
NEXT;
}
2013-10-20 20:29:56 -04:00
CASE(OP_PUSHCONST) {
2014-01-18 08:32:41 -05:00
pic_value self;
struct pic_irep *irep;
self = pic->ci->fp[0];
if (! pic_proc_p(self)) {
pic_error(pic, "logic flaw");
}
irep = pic_proc_ptr(self)->u.irep;
if (! pic_proc_irep_p(pic_proc_ptr(self))) {
2014-01-18 08:32:41 -05:00
pic_error(pic, "logic flaw");
}
PUSH(irep->pool[c.u.i]);
2013-10-20 20:29:56 -04:00
NEXT;
}
2013-10-12 05:48:35 -04:00
CASE(OP_GREF) {
PUSH(pic->globals[c.u.i]);
2013-10-12 05:48:35 -04:00
NEXT;
2013-10-12 01:40:55 -04:00
}
2013-10-12 05:48:35 -04:00
CASE(OP_GSET) {
pic->globals[c.u.i] = POP();
2013-10-12 05:48:35 -04:00
NEXT;
2013-10-12 01:40:55 -04:00
}
2013-10-16 04:20:53 -04:00
CASE(OP_LREF) {
PUSH(pic->ci->fp[c.u.i]);
2013-10-16 04:20:53 -04:00
NEXT;
}
2013-10-23 14:14:32 -04:00
CASE(OP_LSET) {
pic->ci->fp[c.u.i] = POP();
2013-10-23 14:14:32 -04:00
NEXT;
}
2013-10-23 13:04:49 -04:00
CASE(OP_CREF) {
2013-11-04 21:32:09 -05:00
int depth = c.u.r.depth;
2013-10-23 13:04:49 -04:00
struct pic_env *env;
2014-03-23 00:09:35 -04:00
env = pic->ci->up;
while (--depth) {
2013-10-23 13:04:49 -04:00
env = env->up;
}
2014-03-22 23:45:36 -04:00
PUSH(env->regs[c.u.r.idx]);
2013-10-23 13:04:49 -04:00
NEXT;
}
2013-10-23 14:14:32 -04:00
CASE(OP_CSET) {
2013-11-04 21:32:09 -05:00
int depth = c.u.r.depth;
2013-10-23 14:14:32 -04:00
struct pic_env *env;
2014-03-23 00:09:35 -04:00
env = pic->ci->up;
while (--depth) {
2013-10-23 14:14:32 -04:00
env = env->up;
}
2014-03-22 23:45:36 -04:00
env->regs[c.u.r.idx] = POP();
2013-10-23 14:14:32 -04:00
NEXT;
}
2013-10-16 04:42:47 -04:00
CASE(OP_JMP) {
2014-02-03 20:40:03 -05:00
pic->ip += c.u.i;
2013-10-16 04:42:47 -04:00
JUMP;
}
CASE(OP_JMPIF) {
pic_value v;
v = POP();
if (! pic_false_p(v)) {
2014-02-03 20:40:03 -05:00
pic->ip += c.u.i;
2013-10-16 04:42:47 -04:00
JUMP;
}
NEXT;
}
2014-02-02 00:54:47 -05:00
CASE(OP_NOT) {
pic_value v;
v = pic_false_p(POP()) ? pic_true_value() : pic_false_value();
PUSH(v);
NEXT;
}
2013-10-15 06:18:33 -04:00
CASE(OP_CALL) {
pic_value x, v;
pic_callinfo *ci;
2013-10-15 06:18:33 -04:00
struct pic_proc *proc;
2014-02-20 04:38:09 -05:00
if (c.u.i == -1) {
pic->sp += pic->ci[1].retc - 1;
c.u.i = pic->ci[1].retc + 1;
}
L_CALL:
x = pic->sp[-c.u.i];
if (! pic_proc_p(x)) {
2014-03-03 08:44:38 -05:00
pic_errorf(pic, "invalid application: ~s", x);
2013-10-23 14:38:29 -04:00
}
proc = pic_proc_ptr(x);
#if VM_DEBUG
puts("\n== calling proc...");
printf(" proc = ");
pic_debug(pic, pic_obj_value(proc));
puts("");
printf(" argv = (");
for (short i = 1; i < c.u.i; ++i) {
if (i > 1)
printf(" ");
pic_debug(pic, pic->sp[-c.u.i + i]);
}
puts(")");
if (! pic_proc_func_p(proc)) {
2014-03-27 23:29:00 -04:00
printf(" irep = %p\n", proc->u.irep);
2014-03-27 23:34:54 -04:00
printf(" name = %s\n", pic_symbol_name(pic, pic_proc_name(proc)));
2014-03-19 06:44:45 -04:00
pic_dump_irep(proc->u.irep);
}
else {
printf(" cfunc = %p\n", (void *)proc->u.func.f);
2014-03-27 23:34:54 -04:00
printf(" name = %s\n", pic_symbol_name(pic, pic_proc_name(proc)));
}
puts("== end\n");
#endif
2013-10-15 10:29:34 -04:00
ci = PUSHCI();
ci->argc = c.u.i;
2014-02-20 03:19:28 -05:00
ci->retc = 1;
2014-02-03 20:46:36 -05:00
ci->ip = pic->ip;
ci->fp = pic->sp - c.u.i;
ci->env = NULL;
if (pic_proc_func_p(pic_proc_ptr(x))) {
2014-02-20 02:33:18 -05:00
/* invoke! */
pic->sp[0] = proc->u.func.f(pic);
2014-06-22 11:52:36 -04:00
pic->sp += pic->ci->retc;
2014-02-20 02:33:18 -05:00
pic_gc_arena_restore(pic, ai);
goto L_RET;
2013-10-15 22:28:57 -04:00
}
else {
2014-03-22 05:23:33 -04:00
struct pic_irep *irep = proc->u.irep;
2013-10-23 13:51:02 -04:00
int i;
2013-10-27 05:13:36 -04:00
pic_value rest;
2013-10-23 13:51:02 -04:00
2014-03-22 05:23:33 -04:00
if (ci->argc != irep->argc) {
if (! (irep->varg && ci->argc >= irep->argc)) {
pic_errorf(pic, "wrong number of arguments (%d for %d%s)", ci->argc - 1, irep->argc - 1, (irep->varg ? "+" : ""));
2013-10-27 05:13:36 -04:00
}
}
/* prepare rest args */
2014-03-22 05:23:33 -04:00
if (irep->varg) {
2013-10-27 05:13:36 -04:00
rest = pic_nil_value();
2014-03-22 05:23:33 -04:00
for (i = 0; i < ci->argc - irep->argc; ++i) {
2013-10-27 05:13:36 -04:00
pic_gc_protect(pic, v = POP());
rest = pic_cons(pic, v, rest);
}
PUSH(rest);
}
/* prepare local variable area */
2014-03-22 05:23:33 -04:00
if (irep->localc > 0) {
int l = irep->localc;
if (irep->varg) {
--l;
}
for (i = 0; i < l; ++i) {
PUSH(pic_undef_value());
}
}
2013-10-28 21:16:56 -04:00
/* prepare env */
2014-03-23 00:09:35 -04:00
ci->up = proc->env;
ci->regc = irep->capturec;
ci->regs = ci->fp + irep->argc + irep->localc;
2013-10-28 21:16:56 -04:00
2014-03-22 05:23:33 -04:00
pic->ip = irep->code;
2013-10-16 02:30:52 -04:00
pic_gc_arena_restore(pic, ai);
JUMP;
2013-10-15 22:28:57 -04:00
}
2013-10-16 02:30:52 -04:00
}
2013-10-29 21:03:46 -04:00
CASE(OP_TAILCALL) {
int i, argc;
2013-10-29 21:03:46 -04:00
pic_value *argv;
2014-02-20 02:01:29 -05:00
pic_callinfo *ci;
2013-10-29 21:03:46 -04:00
2014-03-23 00:09:35 -04:00
if (pic->ci->env != NULL) {
vm_tear_off(pic);
}
2014-02-20 04:38:09 -05:00
if (c.u.i == -1) {
pic->sp += pic->ci[1].retc - 1;
c.u.i = pic->ci[1].retc + 1;
}
argc = c.u.i;
2013-10-29 21:03:46 -04:00
argv = pic->sp - argc;
for (i = 0; i < argc; ++i) {
pic->ci->fp[i] = argv[i];
}
2014-02-20 02:01:29 -05:00
ci = POPCI();
pic->sp = ci->fp + argc;
pic->ip = ci->ip;
2013-10-29 21:03:46 -04:00
/* c is not changed */
2013-10-29 21:03:46 -04:00
goto L_CALL;
}
2013-10-16 02:30:52 -04:00
CASE(OP_RET) {
2014-02-20 02:01:29 -05:00
int i, retc;
pic_value *retv;
pic_callinfo *ci;
2013-10-16 02:30:52 -04:00
2014-03-23 00:09:35 -04:00
if (pic->ci->env != NULL) {
vm_tear_off(pic);
}
2014-02-20 02:33:18 -05:00
pic->ci->retc = c.u.i;
L_RET:
retc = pic->ci->retc;
2014-02-20 02:01:29 -05:00
retv = pic->sp - retc;
if (retc == 0) {
pic->ci->fp[0] = retv[0]; /* copy at least once */
}
2014-02-20 02:01:29 -05:00
for (i = 0; i < retc; ++i) {
pic->ci->fp[i] = retv[i];
2013-10-20 10:30:01 -04:00
}
2014-02-20 02:01:29 -05:00
ci = POPCI();
pic->sp = ci->fp + 1; /* advance only one! */
2014-02-20 02:01:29 -05:00
pic->ip = ci->ip;
2013-10-15 22:32:30 -04:00
NEXT;
}
CASE(OP_LAMBDA) {
pic_value self;
struct pic_irep *irep;
2013-10-15 22:32:30 -04:00
struct pic_proc *proc;
2013-10-23 13:02:07 -04:00
self = pic->ci->fp[0];
if (! pic_proc_p(self)) {
pic_error(pic, "logic flaw");
}
irep = pic_proc_ptr(self)->u.irep;
if (! pic_proc_irep_p(pic_proc_ptr(self))) {
pic_error(pic, "logic flaw");
}
2014-03-23 00:09:35 -04:00
if (pic->ci->env == NULL) {
vm_push_env(pic);
}
proc = pic_proc_new_irep(pic, irep->irep[c.u.i], pic->ci->env);
2013-10-15 22:32:30 -04:00
PUSH(pic_obj_value(proc));
pic_gc_arena_restore(pic, ai);
2013-10-15 06:18:33 -04:00
NEXT;
}
2013-10-12 05:48:35 -04:00
CASE(OP_CONS) {
2013-10-12 00:06:02 -04:00
pic_value a, b;
2013-10-13 04:29:21 -04:00
pic_gc_protect(pic, b = POP());
pic_gc_protect(pic, a = POP());
PUSH(pic_cons(pic, a, b));
2013-10-13 04:29:21 -04:00
pic_gc_arena_restore(pic, ai);
2013-10-12 05:48:35 -04:00
NEXT;
2013-10-12 00:06:02 -04:00
}
2013-10-19 14:48:06 -04:00
CASE(OP_CAR) {
pic_value p;
p = POP();
PUSH(pic_car(pic, p));
NEXT;
}
CASE(OP_CDR) {
pic_value p;
p = POP();
PUSH(pic_cdr(pic, p));
NEXT;
}
2013-10-19 14:53:02 -04:00
CASE(OP_NILP) {
pic_value p;
p = POP();
PUSH(pic_bool_value(pic_nil_p(p)));
NEXT;
}
2013-10-27 11:21:24 -04:00
#define DEFINE_ARITH_OP(opcode, op, guard) \
2013-10-27 11:21:24 -04:00
CASE(opcode) { \
pic_value a, b; \
b = POP(); \
a = POP(); \
if (pic_int_p(a) && pic_int_p(b)) { \
2013-10-28 08:26:39 -04:00
double f = (double)pic_int(a) op (double)pic_int(b); \
if (INT_MIN <= f && f <= INT_MAX && (guard)) { \
2013-10-28 08:26:39 -04:00
PUSH(pic_int_value((int)f)); \
} \
else { \
PUSH(pic_float_value(f)); \
} \
2013-10-27 11:21:24 -04:00
} \
else if (pic_float_p(a) && pic_float_p(b)) { \
PUSH(pic_float_value(pic_float(a) op pic_float(b))); \
} \
else if (pic_int_p(a) && pic_float_p(b)) { \
PUSH(pic_float_value(pic_int(a) op pic_float(b))); \
} \
else if (pic_float_p(a) && pic_int_p(b)) { \
PUSH(pic_float_value(pic_float(a) op pic_int(b))); \
} \
else { \
pic_error(pic, #op " got non-number operands"); \
2013-10-27 11:21:24 -04:00
} \
NEXT; \
2013-10-15 08:29:07 -04:00
}
2013-10-27 11:21:24 -04:00
DEFINE_ARITH_OP(OP_ADD, +, true);
DEFINE_ARITH_OP(OP_SUB, -, true);
DEFINE_ARITH_OP(OP_MUL, *, true);
DEFINE_ARITH_OP(OP_DIV, /, f == round(f));
2013-10-27 11:21:24 -04:00
2013-11-06 22:17:37 -05:00
CASE(OP_MINUS) {
pic_value n;
n = POP();
if (pic_int_p(n)) {
PUSH(pic_int_value(-pic_int(n)));
}
else if (pic_float_p(n)) {
PUSH(pic_float_value(-pic_float(n)));
}
else {
pic_error(pic, "unary - got a non-number operand");
2013-11-06 22:17:37 -05:00
}
NEXT;
}
2013-10-27 11:21:24 -04:00
#define DEFINE_COMP_OP(opcode, op) \
CASE(opcode) { \
pic_value a, b; \
b = POP(); \
a = POP(); \
if (pic_int_p(a) && pic_int_p(b)) { \
PUSH(pic_bool_value(pic_int(a) op pic_int(b))); \
} \
else if (pic_float_p(a) && pic_float_p(b)) { \
PUSH(pic_bool_value(pic_float(a) op pic_float(b))); \
} \
2013-11-13 03:40:31 -05:00
else if (pic_int_p(a) && pic_float_p(b)) { \
2013-10-27 11:21:24 -04:00
PUSH(pic_bool_value(pic_int(a) op pic_float(b))); \
} \
else if (pic_float_p(a) && pic_int_p(b)) { \
PUSH(pic_bool_value(pic_float(a) op pic_int(b))); \
} \
else { \
pic_error(pic, #op " got non-number operands"); \
2013-10-27 11:21:24 -04:00
} \
NEXT; \
2013-10-24 08:10:13 -04:00
}
2013-10-27 11:21:24 -04:00
DEFINE_COMP_OP(OP_EQ, ==);
DEFINE_COMP_OP(OP_LT, <);
DEFINE_COMP_OP(OP_LE, <=);
2013-10-12 05:48:35 -04:00
CASE(OP_STOP) {
2013-10-20 00:07:14 -04:00
#if VM_DEBUG
2013-10-20 04:26:18 -04:00
puts("**VM END STATE**");
printf("stbase\t= %p\nsp\t= %p\n", (void *)stbase, (void *)pic->sp);
printf("cibase\t= %p\nci\t= %p\n", (void *)cibase, (void *)pic->ci);
2014-03-24 02:15:08 -04:00
if (stbase < pic->sp - 1) {
2013-10-20 04:26:18 -04:00
pic_value *sp;
printf("* stack trace:");
for (sp = stbase; pic->sp != sp; ++sp) {
2013-10-20 04:26:18 -04:00
pic_debug(pic, *sp);
puts("");
}
}
2014-03-24 02:15:08 -04:00
if (stbase > pic->sp - 1) {
2013-10-29 21:05:21 -04:00
puts("*** stack underflow!");
}
2013-10-16 02:30:52 -04:00
#endif
2014-03-24 02:15:08 -04:00
return pic_gc_protect(pic, POP());
2013-10-20 04:26:18 -04:00
}
} VM_LOOP_END;
2013-10-12 01:40:27 -04:00
}
2014-02-06 00:22:29 -05:00
pic_value
pic_apply_trampoline(pic_state *pic, struct pic_proc *proc, pic_value args)
2014-02-06 00:22:29 -05:00
{
2014-06-22 11:56:43 -04:00
static const pic_code iseq = { OP_TAILCALL, { .i = -1 } };
2014-06-22 11:54:11 -04:00
2014-06-22 11:52:36 -04:00
pic_value v, *sp;
2014-02-06 19:49:49 -05:00
pic_callinfo *ci;
2014-02-06 00:22:29 -05:00
2014-06-22 11:52:36 -04:00
*pic->sp++ = pic_obj_value(proc);
2014-02-06 00:22:29 -05:00
2014-06-22 11:52:36 -04:00
sp = pic->sp;
pic_for_each (v, args) {
*sp++ = v;
2014-02-06 00:22:29 -05:00
}
2014-02-06 19:49:49 -05:00
ci = PUSHCI();
2014-06-22 11:56:43 -04:00
ci->ip = (pic_code *)&iseq - 1;
2014-06-22 11:52:36 -04:00
ci->fp = pic->sp;
ci->retc = pic_length(pic, args);
return pic_obj_value(proc);
2014-02-06 00:22:29 -05:00
}
2014-03-03 08:23:10 -05:00
pic_value
pic_eval(pic_state *pic, pic_value program)
{
struct pic_proc *proc;
proc = pic_compile(pic, program);
return pic_apply(pic, proc, pic_nil_value());
}