/* Copyright 1989, 1990 by James Aspnes, David Applegate, and Bennet Yee */
/* See the file COPYING for distribution information */
#include "db.h"
#include "bytecode.h"
#include "set.h"
#include "globals.h"

#define BINOP(token, op) case token: sp--; *sp = sp[0] op sp[1]; break;
#define FUNC1(token, f) case token: *sp = f(*sp); break;
#define FUNC2(token, f) case token: sp--; *sp = f(sp[0], sp[1]); break;
#define FUNC3(token, f) \
  case token: sp-=2; *sp = f(sp[0], sp[1], sp[2]); break;
#define VALUE(token, v) case token: *++sp = v; break;
#define SLOT(token, s) case token: *sp = safe_get(*sp, s); break;
#define PROC1(token, f) case token: f(*sp--); break;

extern set lookup_setvar(datum, datum);

int run_code(byte *c)
{
    datum next;
    byte *pc;
    datum stack[STACKSIZE];
    datum *sp;
    int count;
    set savelist;
    set_iterator next_iterator;

    if(c == 0) return -1;

    next = NOTHING;
    savelist = empty_set(); 

    for(sp = stack, pc = c, count = 0;
	*pc != RETURN_OP;
	pc++, count++) {
	switch(*pc) {
	    FUNC2(MOVE_OP, move);
	    FUNC2(MATCHES_OP, matches);
	    FUNC2(UNSET_OP, unset);
	    FUNC3(SET_OP, set_variable);
	    FUNC3(SET_STRING_OP, set_string);
	    FUNC2(UNSET_ACTION_OP, unset_action);
	    FUNC3(SET_ACTION_OP, set_action);
	    FUNC2(LOOKUP_OP, lookup);
	    FUNC2(LOOKUP_ACTION_OP, lookup_action);
	    FUNC1(NOT_OP, !);
	    VALUE(TRUE_OP, 1);
	    VALUE(FALSE_OP, 0);
	    VALUE(ME_OP, me);
	    VALUE(YOU_OP, you);
	    VALUE(NEXT_OP, next);
	    VALUE(TEXT_OP, text);
	    VALUE(MTEXT_OP, mtext);
	    SLOT(LOC_OP, location);
	    VALUE(TELL_OP, do_tell());
	    VALUE(CREATE_OP, create());
	    FUNC1(DESTROY_OP, destroy);
	    PROC1(TELL_INIT_OP, tell_init);
	    PROC1(ADD_BUF_OP, add_buffer);
	    PROC1(ADD_NUM_BUF_OP, add_num_buffer);
	    PROC1(ADD_TIME_BUF_OP, add_time_buffer);
	    FUNC1(SYSCALL_OP, do_syscall);
	    FUNC1(NEGATE_OP, -);
	    FUNC2(CLEAR_SET_OP, clear_set_var);
	    FUNC3(ADD_SET_OP, add_to);
	    FUNC3(DEL_SET_OP, take_from);
	    FUNC2(COUNT_OP, count_set_var);
	    FUNC3(CONTAINS_OP, contains);
	    VALUE(TIME_OP, time(0));
	    FUNC1(DELAY_OP, delay);
	    FUNC2(CONCAT_OP, concat);
	    FUNC1(NUM_TO_STRING_OP, num_to_string);

	    /* binary numeric operations */
	    BINOP(PLUS_OP, +);
	    BINOP(MINUS_OP, -);
	    BINOP(TIMES_OP, *);
	    BINOP(DIV_OP, /);
	    BINOP(MOD_OP, %);
	    BINOP(LT_OP, <);
	    BINOP(GT_OP, >);
	    BINOP(LEQ_OP, <=);
	    BINOP(GEQ_OP, >=);
	    BINOP(EQ_OP, ==);
	    BINOP(NEQ_OP, !=);

	  case EXIT_OP:
	    goto exit;
	  case JUMP_OP:
	    pc += ARG_VALUE(pc+1);
	    break;
	  case JUMP_BACK_OP:
	    pc -= ARG_VALUE(pc+1);
	    break;
	  case JUMP_IF_OP:
	    if(*sp-- != NOTHING) {
		pc += ARG_VALUE(pc+1);
	    } else {
		pc += ARG_WIDTH;	
	    }
	    break;
	  case DO_SET_INIT_OP:
	    free_set(savelist);
	    sp -= 2;
	    savelist = copy_set(lookup_setvar(sp[1], sp[2]));
	    next = set_first(savelist, &next_iterator);
	    break;
	  case MATCHING_DO_SET_INIT_OP:
	    free_set(savelist);
	    sp -= 3;
	    savelist = lookup_setvar(sp[1], sp[2]);
	    set_build_name_list(savelist); /* build before copy */
	    savelist = copy_set(savelist);
	    next = set_first_match(savelist, &next_iterator, sp[3]);
	    break;
	  case DO_NEXT_OP:
	    next = set_next(savelist, &next_iterator);
	    break;
	  case MATCHING_DO_NEXT_OP:
	    next = set_next_match(savelist, &next_iterator);
	    break;
	  case LITERAL_OP:
	    *++sp = ARG_VALUE(pc+1);
	    pc += ARG_WIDTH;		
	    break;
	  case DROP_OP:
	    sp--;
	    break;
	  case DUP_OP:
	    sp[1] = sp[0];
	    sp++;
	    break;
	  case RANDOM_OP:
	    *++sp = RAND();
	    break;
	  default:
	    abort();		
	}
    }

  exit:
    return count;
}
	  
int run_action(datum action)
{
    const char *s;
    datum *code;
    char buf[MAX_STRLEN];

    if((code = get_compiled_string(action)) == 0) {
	/* try compiling it */
	if((s = string(action)) == 0) {
	    return -1;		/* we lose completely */
	} else {
	    code = compile(s);
	    if(code == 0) {
		sprintf(buf, LOSE_CODE, compile_error);
		code = compile(buf);
	    }
	    set_compiled_string(action, code);
	}
    }

    return run_code(code);
}