/* 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"
#include "externs.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);
}